﻿using NPOI.POIFS.Properties;
using NSoup.Nodes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using static IronPython.Modules._ast;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor.Widgets.MindMap
{
    /// <summary>
    /// MindMap.xaml 的交互逻辑
    /// </summary>
    public partial class MindMapView : UserControl
    {
        public MindMapView()
        {
            InitializeComponent();
            this.TitleDefaultFontSize = 24;
        }

        /// <summary>
        /// 这个属性主要用以计算当前文档中链接的图像、文件的实际物理路径。
        /// </summary>
        public string MasterMarkdownFilePath { get; set; }

        public double TitleDefaultFontSize
        {
            get { return (double)GetValue(TitleDefaultFontSizeProperty); }
            set { SetValue(TitleDefaultFontSizeProperty, value); }
        }

        public static readonly DependencyProperty TitleDefaultFontSizeProperty =
            DependencyProperty.Register("TitleDefaultFontSize", typeof(double), typeof(MindMapView), new PropertyMetadata(double.NaN));


        public Brush LayoutTitleMarkFill
        {
            get { return (Brush)GetValue(LayoutTitleMarkFillProperty); }
            set { SetValue(LayoutTitleMarkFillProperty, value); }
        }

        public static readonly DependencyProperty LayoutTitleMarkFillProperty =
            DependencyProperty.Register("LayoutTitleMarkFill", typeof(Brush), typeof(MindMapView), new PropertyMetadata(Brushes.White));

        public Brush TitleDefaultForeground
        {
            get { return (Brush)GetValue(TitleDefaultForegroundProperty); }
            set
            {
                SetValue(TitleDefaultForegroundProperty, value);

                foreach (var ue in this.MainCanvas.Children)
                {
                    var title = ue as MindMapTitle;
                    if (title == null) continue;

                    title.RefreshTitleForeground();
                }
            }
        }

        public static readonly DependencyProperty TitleDefaultForegroundProperty =
            DependencyProperty.Register("TitleDefaultForeground", typeof(Brush), typeof(MindMapView), new PropertyMetadata(Brushes.Black));

        public Brush TitleDefaultActiveBrush
        {
            get { return (Brush)GetValue(TitleDefaultActiveBrushProperty); }
            set
            {
                SetValue(TitleDefaultActiveBrushProperty, value);

                foreach (var ue in this.MainCanvas.Children)
                {
                    var title = ue as MindMapTitle;
                    if (title == null) continue;

                    title.RefreshSelectionBrush();
                }
            }
        }

        public static readonly DependencyProperty TitleDefaultActiveBrushProperty =
            DependencyProperty.Register("TitleDefaultActiveBrush", typeof(Brush), typeof(MindMapView), new PropertyMetadata(Brushes.Yellow));

        public Brush TitleDefaultBackground
        {
            get { return (Brush)GetValue(TitleDefaultBackgroundProperty); }
            set
            {
                SetValue(TitleDefaultBackgroundProperty, value);

                foreach (var ue in this.MainCanvas.Children)
                {
                    var title = ue as MindMapTitle;
                    if (title == null || title.Role != MindMapTitle.TitleRole.DisplayCenterTitle) continue;

                    title.RefreshTitleBackground();
                }
            }
        }

        public static readonly DependencyProperty TitleDefaultBackgroundProperty =
            DependencyProperty.Register("TitleDefaultBackground", typeof(Brush), typeof(MindMapView), new PropertyMetadata(Brushes.WhiteSmoke));

        public Brush AncestorsTitleDefaultBackground
        {
            get { return (Brush)GetValue(AncestorsTitleDefaultBackgroundProperty); }
            set
            {
                SetValue(AncestorsTitleDefaultBackgroundProperty, value);

                foreach (var ue in this.MainCanvas.Children)
                {
                    var title = ue as MindMapTitle;
                    if (title == null || title.Role != MindMapTitle.TitleRole.AncestorsTitle) continue;

                    title.RefreshTitleBackground();
                }
            }
        }

        public static readonly DependencyProperty AncestorsTitleDefaultBackgroundProperty =
            DependencyProperty.Register("AncestorsTitleDefaultBackground", typeof(Brush), typeof(MindMapView), new PropertyMetadata(Brushes.WhiteSmoke));

        public Brush ChildTitleDefaultBackground
        {
            get { return (Brush)GetValue(ChildTitleDefaultBackgroundProperty); }
            set
            {
                SetValue(ChildTitleDefaultBackgroundProperty, value);

                foreach (var ue in this.MainCanvas.Children)
                {
                    var title = ue as MindMapTitle;
                    if (title == null || title.Role != MindMapTitle.TitleRole.ChildTitle) continue;

                    title.RefreshTitleBackground();
                }
            }
        }

        public static readonly DependencyProperty ChildTitleDefaultBackgroundProperty =
            DependencyProperty.Register("ChildTitleDefaultBackground", typeof(Brush), typeof(MindMapView), new PropertyMetadata(Brushes.WhiteSmoke));

        public Brush TitleDefaultCommentForegeound
        {
            get { return (Brush)GetValue(TitleDefaultCommentForegeoundProperty); }
            set { SetValue(TitleDefaultCommentForegeoundProperty, value); }
        }

        public Brush TitleDefaultBorderBrush
        {
            get { return (Brush)GetValue(TitleDefaultBorderBrushProperty); }
            set { SetValue(TitleDefaultBorderBrushProperty, value); }
        }

        public static readonly DependencyProperty TitleDefaultBorderBrushProperty =
            DependencyProperty.Register("TitleDefaultBorderBrush", typeof(Brush), typeof(MindMapView), new PropertyMetadata(Brushes.WhiteSmoke));

        public static readonly DependencyProperty TitleDefaultCommentForegeoundProperty =
            DependencyProperty.Register("TitleDefaultCommentForegeound", typeof(Brush), typeof(MindMapView), new PropertyMetadata(Brushes.DarkGreen));

        public Brush TitleLinkStroke
        {
            get { return (Brush)GetValue(TitleDefaultLinkStrokeProperty); }
            set
            {
                SetValue(TitleDefaultLinkStrokeProperty, value);

                foreach (var ue in this.MainCanvas.Children)
                {
                    var title = ue as MindMapTitle;
                    if (title == null) continue;

                    title.LinkToParentPath.Stroke = value;
                }
            }
        }

        public static readonly DependencyProperty TitleDefaultLinkStrokeProperty =
            DependencyProperty.Register("TitleDefaultLinkStrokeProperty", typeof(Brush), typeof(MindMapView), new PropertyMetadata(Brushes.Black));

        private bool isToolBarVisible = true;

        public bool IsToolBarVisible
        {
            get { return isToolBarVisible; }
            set
            {
                isToolBarVisible = value;
                if (isToolBarVisible)
                {
                    btnShowTools.Visibility =
                        spDisplayTools.Visibility = Visibility.Visible;
                }
                else
                {
                    btnShowTools.Visibility =
                        spDisplayTools.Visibility = Visibility.Collapsed;
                }
            }
        }

        public static readonly DependencyProperty IsToolBarVisibleProperty =
            DependencyProperty.Register("IsToolBarVisible", typeof(bool), typeof(MindMapView), new PropertyMetadata(true));

        private List<TreeElement> rootElements = null;
        /// <summary>
        /// 一个树型文字表可能由多个根节点组成，故应拆分为多张思维导图。
        /// </summary>
        public List<TreeElement> RootElements
        {
            get { return this.rootElements; }
            set
            {
                this.rootElements = value;
                if (rootElements != null && rootElements.Count > 0)
                {
                    this.DisplayMindMap(rootElements[0] as RootElement);
                }
            }
        }

        /// <summary>
        /// 当前显示的元素对应的 MindMapTitle。
        /// </summary>
        public MindMapTitle DisplayTitle { get; set; }
        /// <summary>
        /// 当前选中的 MindMapTitle，用于切换选取焦点框。
        /// </summary>
        public MindMapTitle SelectedTitle { get; set; }

        private ViewType displayView = ViewType.Default;

        /// <summary>
        /// 当前控件的视图模式。
        /// </summary>
        public ViewType DisplayView
        {
            get { return this.displayView; }
            set
            {
                var oldValue = this.displayView;
                var newValue = value;
                this.displayView = value;
                OnViewtypeChaged(this, new ViewtypeChangedArgs() { OldViewType = oldValue, NewViewType = value });
            }
        }

        public event EventHandler<ViewtypeChangedArgs> ViewtypeChanged = null;

        protected void OnViewtypeChaged(object sender, ViewtypeChangedArgs e)
        {
            if (ViewtypeChanged != null)
            {
                ViewtypeChanged(sender, e);
            }
        }

        public void DisplayMindMap()
        {
            if (RootElements != null && RootElements.Count > 0)
            {
                DisplayMindMap(RootElements[0]);
            }
        }

        public event EventHandler<DisplayElementChangedArgs> DisplayElementChanged = null;

        protected void OnDisplayElementChanged(object sender, DisplayElementChangedArgs e)
        {
            if (DisplayElementChanged != null)
            {
                DisplayElementChanged(sender, e);
            }
        }

        public event EventHandler<PreviewedArgs> Previewed = null;

        protected void OnPreviewed(object sender, PreviewedArgs e)
        {
            if (Previewed != null)
            {
                Previewed(this, new PreviewedArgs() { ViewType = this.displayView });
            }
        }

        /// <summary>
        /// 根据 某个 Element 的值生成思维导图.
        /// </summary>
        /// <param name="element"></param>
        public void DisplayMindMap(TreeElement element, bool showAllSubElements = false)
        {
            if (element == null) return;

            var rootElement = element as RootElement;
            if (rootElement != null)
            {
                curDisplayingRootElement = rootElement;
            }
            else
            {
                curDisplayingRootElement = element.RootElement;
            }

            var oldElement = curDisplayingElement;

            curDisplayingElement = element;

            if (curDisplayingElement == null || curDisplayingRootElement == null) return;

            if (element.ViewType.HasValue)
            {
                if (element.ViewType.Value == ViewType.ListOfAncestors && element.ParentElement == null)  // 根元素不能显示祖先组列表！
                {
                    DisplayView = ViewType.List;              // 用近似的 List
                }
                else
                {
                    DisplayView = element.ViewType.Value;
                }
            }

            switch (DisplayView)
            {
                case ViewType.ListOfAncestors:
                    {
                        DisplayWithAncestorsList(element);
                        break;
                    }
                case ViewType.List:
                    {
                        DisplayWithListView(element);
                        break;
                    }
                case ViewType.OrgUp:
                case ViewType.OrgDown:
                case ViewType.OrgLeft:
                case ViewType.OrgRight:
                    {
                        DisplayWithOrgView(element, DisplayView);
                        break;
                    }
                case ViewType.LeftFish:
                case ViewType.RightFish:
                    {
                        DisplayWithFishView(element, DisplayView);
                        break;
                    }
                case ViewType.MindMap:
                    {
                        DisplayAllSubElementsWithDefaultView(element);
                        break;
                    }
                //case ViewType.Default:
                default:
                    {
                        DisplayWithDefaultView(element);
                        break;
                    }
            }

            var newElement = curDisplayingElement;
            OnDisplayElementChanged(this, new DisplayElementChangedArgs() { OldElement = oldElement, NewElement = newElement, });
        }

        private void DisplayWithOrgView(TreeElement element, ViewType displayView)
        {
            switch (displayView)
            {
                case ViewType.OrgRight:
                    {
                        DisplayWithRightOrgView(element);
                        break;
                    }
                case ViewType.OrgLeft:
                    {
                        DisplayWithLeftOrgView(element);
                        break;
                    }
                case ViewType.OrgUp:
                    {
                        DisplayWithUpOrgView(element);
                        break;
                    }
                case ViewType.OrgDown:
                    {
                        DisplayWithDownOrgView(element);
                        break;
                    }
            }
        }

        private void DisplayWithRightOrgView(TreeElement element)
        {
            if (element == null) return;
            if (element.Children.Count <= 0) return;

            HideAndRemoveTitles();

            var parent = element.ParentElement;
            var children = element.Children;

            MindMapTitle parentTitle = null;
            if (parent != null)
            {
                parentTitle = new MindMapTitle(this, element.ParentElement, MindMapTitle.TitleRole.AncestorsTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.AncestorsTitle, };
                parentTitle.IsSelectedChanged += Title_IsSelectedChanged;
                this.MainCanvas.Children.Add(parentTitle);
                parentTitle.InvalidateMeasure();
                parentTitle.UpdateLayout();
            }

            var displayCenterTtile = new MindMapTitle(this, element, MindMapTitle.TitleRole.DisplayCenterTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.DisplayCenterTitle, };
            displayCenterTtile.IsSelectedChanged += Title_IsSelectedChanged;
            this.MainCanvas.Children.Add(displayCenterTtile);
            displayCenterTtile.InvalidateMeasure();
            displayCenterTtile.UpdateLayout();

            this.SelectedTitle = displayCenterTtile;
            displayCenterTtile.IsSelectedTitle = true;

            // 1. 先生成所有标题，放到容器（Canvas）中，这样就可得知所有标题所需要的空间尺寸
            List<MindMapTitle> childTitles = null;
            if (children.Count > 0)
            {
                childTitles = new List<MindMapTitle>();
                foreach (var te in children)
                {
                    var newChildTitle = new MindMapTitle(this, te, MindMapTitle.TitleRole.ChildTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.ChildTitle, };
                    newChildTitle.IsSelectedChanged += Title_IsSelectedChanged;
                    childTitles.Add(newChildTitle);
                    this.MainCanvas.Children.Add(newChildTitle);
                    newChildTitle.InvalidateMeasure();
                    newChildTitle.UpdateLayout();
                }
            }

            // 2. 计算子节点需要的尺寸，这需要考虑布局
            var spacing = baseSpacingTimes * this.TitleDefaultFontSize;

            var width1 = displayCenterTtile.ActualWidth;
            var height1 = displayCenterTtile.ActualHeight;
            if (parent != null)
            {
                width1 = Math.Max(parentTitle.ActualWidth, displayCenterTtile.ActualWidth);
                height1 += (parentTitle.ActualHeight + spacing) * 2;   // 为什么 * 2? 这是为了保证当前标题总在垂直居中的位置
            }

            var height2 = 0.0;
            var width2 = 0.0;
            if (childTitles.Count > 0)
            {
                foreach (var childTitle in childTitles)
                {
                    height2 += childTitle.ActualHeight;
                    width2 = Math.Max(width2, childTitle.ActualWidth);
                }
                height2 += spacing * (childTitles.Count - 1);
            }

            var height = Math.Max(height1, height2);

            MainCanvas.Width = spacing + width1 + spacing * childTitles.Count + width2 + spacing;
            MainCanvas.Height = spacing + height + spacing;

            Canvas.SetTop(displayCenterTtile, displayCenterTtile.Top = spacing + height / 2 - displayCenterTtile.ActualHeight / 2);
            Canvas.SetLeft(displayCenterTtile, displayCenterTtile.Left = spacing + width1 - displayCenterTtile.ActualWidth);

            // 透明动画
            var animationOfDisplayCenterTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
            displayCenterTtile.BeginAnimation(MindMapTitle.OpacityProperty, animationOfDisplayCenterTitle);

            if (parentTitle != null)
            {
                Canvas.SetTop(parentTitle, parentTitle.Top = displayCenterTtile.TopCenter.Y - spacing - parentTitle.ActualHeight);
                Canvas.SetLeft(parentTitle, parentTitle.Left = displayCenterTtile.RightCenter.X - parentTitle.ActualWidth);         // 右对齐比居中对齐更不容易出现“线条与父标题交叉”的问题

                // 透明动画
                var animationOfParentTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                parentTitle.BeginAnimation(MindMapTitle.OpacityProperty, animationOfParentTitle);
            }

            var baseHeight = spacing;
            var baseWidth = spacing + width1 + spacing * childTitles.Count;
            if (childTitles.Count > 0)
            {
                foreach (var childTitle in childTitles)
                {
                    Canvas.SetTop(childTitle, childTitle.Top = baseHeight);
                    Canvas.SetLeft(childTitle, childTitle.Left = baseWidth);
                }
            }

            if (parentTitle != null)
            {
                var pt1 = displayCenterTtile.LinkPointStart = displayCenterTtile.TopCenter;
                var pt2 = displayCenterTtile.LinkPoint3 = parentTitle.BottomCenter;
                displayCenterTtile.LinkPoint1 = new Point(displayCenterTtile.TopCenter.X, pt1.Y + (pt2.Y - pt1.Y) / 2);
                displayCenterTtile.LinkPoint2 = new Point(parentTitle.BottomCenter.X, displayCenterTtile.LinkPoint1.Y);
                displayCenterTtile.RefreshLine(null);
                MainCanvas.Children.Add(displayCenterTtile.LinkToParentPath);

                // 透明动画
                var animationOfToParentLinkPath = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                displayCenterTtile.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animationOfToParentLinkPath);
            }

            baseHeight = 0;
            if (height1 > height2)
            {
                baseHeight = Math.Abs(height1 - height2) / 2;       // offset
            }

            foreach (var childTitle in childTitles)
            {
                baseHeight += spacing;   // 每个子级标题顶部应有一个 spacing
                Canvas.SetTop(childTitle, childTitle.Top = baseHeight);
                Canvas.SetLeft(childTitle, childTitle.Left = baseWidth);
                Canvas.SetZIndex(childTitle, int.MaxValue);
                baseHeight += childTitle.ActualHeight;

                childTitle.LinkPointStart = new Point(childTitle.LeftCenter.X, childTitle.LeftCenter.Y);
                childTitle.LinkPoint1 = new Point(displayCenterTtile.RightCenter.X + (childTitle.Left - displayCenterTtile.RightCenter.X) / 2, childTitle.LeftCenter.Y);
                childTitle.LinkPoint2 = new Point(childTitle.LinkPoint1.X, displayCenterTtile.RightCenter.Y);
                childTitle.LinkPoint3 = displayCenterTtile.RightCenter;

                childTitle.RefreshLine(null);
                Canvas.SetZIndex(childTitle.LinkToParentPath, 1);
                MainCanvas.Children.Add(childTitle.LinkToParentPath);

                // 透明动画
                var animationOfChildTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                childTitle.BeginAnimation(MindMapTitle.OpacityProperty, animationOfChildTitle);

                var animationOfChildLinkPath = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                childTitle.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animationOfChildLinkPath);
            }

            displayCenterTtile.IsSelectedTitle = true;
            this.DisplayTitle = displayCenterTtile;
            this.SelectedTitle = displayCenterTtile;

#if DEBUG
            MainCanvas.Background = Brushes.Red;
#endif
        }

        private void DisplayWithLeftOrgView(TreeElement element)
        {
            if (element == null) return;
            if (element.Children.Count <= 0) return;

            HideAndRemoveTitles();

            var parent = element.ParentElement;
            var children = element.Children;

            MindMapTitle parentTitle = null;
            if (parent != null)
            {
                parentTitle = new MindMapTitle(this, element.ParentElement, MindMapTitle.TitleRole.AncestorsTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.AncestorsTitle, };
                parentTitle.IsSelectedChanged += Title_IsSelectedChanged;
                this.MainCanvas.Children.Add(parentTitle);
                parentTitle.InvalidateMeasure();
                parentTitle.UpdateLayout();
            }

            var displayCenterTtile = new MindMapTitle(this, element, MindMapTitle.TitleRole.DisplayCenterTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.DisplayCenterTitle, };
            displayCenterTtile.IsSelectedChanged += Title_IsSelectedChanged;
            this.MainCanvas.Children.Add(displayCenterTtile);
            displayCenterTtile.InvalidateMeasure();
            displayCenterTtile.UpdateLayout();

            this.SelectedTitle = displayCenterTtile;
            displayCenterTtile.IsSelectedTitle = true;

            // 1. 先生成所有标题，放到容器（Canvas）中，这样就可得知所有标题所需要的空间尺寸
            List<MindMapTitle> childTitles = null;
            if (children.Count > 0)
            {
                childTitles = new List<MindMapTitle>();
                foreach (var te in children)
                {
                    var newChildTitle = new MindMapTitle(this, te, MindMapTitle.TitleRole.ChildTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.ChildTitle, };
                    newChildTitle.IsSelectedChanged += Title_IsSelectedChanged;
                    childTitles.Add(newChildTitle);
                    this.MainCanvas.Children.Add(newChildTitle);
                    newChildTitle.InvalidateMeasure();
                    newChildTitle.UpdateLayout();
                }
            }

            // 2. 计算子节点需要的尺寸，这需要考虑布局
            var spacing = baseSpacingTimes * this.TitleDefaultFontSize;

            var width1 = displayCenterTtile.ActualWidth;
            var height1 = displayCenterTtile.ActualHeight;
            if (parent != null)
            {
                width1 = Math.Max(parentTitle.ActualWidth, displayCenterTtile.ActualWidth);
                height1 += (parentTitle.ActualHeight + spacing) * 2;   // 为什么 * 2? 这是为了保证当前标题总在垂直居中的位置
            }

            var height2 = 0.0;
            var width2 = 0.0;
            if (childTitles.Count > 0)
            {
                foreach (var childTitle in childTitles)
                {
                    height2 += childTitle.ActualHeight;
                    width2 = Math.Max(width2, childTitle.ActualWidth);
                }
                height2 += spacing * (childTitles.Count - 1);
            }

            var height = Math.Max(height1, height2);

            MainCanvas.Width = spacing + width1 + spacing * childTitles.Count + width2 + spacing;
            MainCanvas.Height = spacing + height + spacing;

            Canvas.SetTop(displayCenterTtile, displayCenterTtile.Top = spacing + height / 2 - displayCenterTtile.ActualHeight / 2);
            Canvas.SetLeft(displayCenterTtile, displayCenterTtile.Left = spacing + width2 + childTitles.Count * spacing);

            // 透明动画
            var animationOfDisplayCenterTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
            displayCenterTtile.BeginAnimation(MindMapTitle.OpacityProperty, animationOfDisplayCenterTitle);

            if (parentTitle != null)
            {
                Canvas.SetTop(parentTitle, parentTitle.Top = displayCenterTtile.TopCenter.Y - spacing - parentTitle.ActualHeight);
                Canvas.SetLeft(parentTitle, parentTitle.Left = displayCenterTtile.LeftCenter.X);                     // 左对齐

                // 透明动画
                var animationOfParentTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                parentTitle.BeginAnimation(MindMapTitle.OpacityProperty, animationOfParentTitle);
            }

            var baseHeight = spacing;
            var baseWidth = spacing + width2 + spacing * childTitles.Count;
            if (childTitles.Count > 0)
            {
                foreach (var childTitle in childTitles)
                {
                    Canvas.SetTop(childTitle, childTitle.Top = baseHeight);
                    Canvas.SetLeft(childTitle, childTitle.Left = baseWidth);
                }
            }

            if (parentTitle != null)
            {
                var pt1 = displayCenterTtile.LinkPointStart = displayCenterTtile.TopCenter;
                var pt2 = displayCenterTtile.LinkPoint3 = parentTitle.BottomCenter;
                displayCenterTtile.LinkPoint1 = new Point(displayCenterTtile.TopCenter.X, pt1.Y + (pt2.Y - pt1.Y) / 2);
                displayCenterTtile.LinkPoint2 = new Point(parentTitle.BottomCenter.X, displayCenterTtile.LinkPoint1.Y);
                displayCenterTtile.RefreshLine(null);
                MainCanvas.Children.Add(displayCenterTtile.LinkToParentPath);

                // 透明动画
                var animationOfToParentLinkPath = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                displayCenterTtile.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animationOfToParentLinkPath);
            }

            baseHeight = 0;
            if (height1 > height2)
            {
                baseHeight = Math.Abs(height1 - height2) / 2;       // offset
            }

            baseWidth = spacing + width2;
            foreach (var childTitle in childTitles)
            {
                baseHeight += spacing;   // 每个子级标题顶部应有一个 spacing
                Canvas.SetTop(childTitle, childTitle.Top = baseHeight);
                Canvas.SetLeft(childTitle, childTitle.Left = baseWidth - childTitle.ActualWidth);
                Canvas.SetZIndex(childTitle, int.MaxValue);
                baseHeight += childTitle.ActualHeight;

                childTitle.LinkPointStart = new Point(childTitle.RightCenter.X, childTitle.RightCenter.Y);
                childTitle.LinkPoint1 = new Point(displayCenterTtile.LeftCenter.X - (displayCenterTtile.LeftCenter.X - childTitle.RightCenter.X) / 2, childTitle.RightCenter.Y);
                childTitle.LinkPoint2 = new Point(childTitle.LinkPoint1.X, displayCenterTtile.LeftCenter.Y);
                childTitle.LinkPoint3 = displayCenterTtile.LeftCenter;

                childTitle.RefreshLine(null);
                Canvas.SetZIndex(childTitle.LinkToParentPath, 1);
                MainCanvas.Children.Add(childTitle.LinkToParentPath);

                // 透明动画
                var animationOfChildTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                childTitle.BeginAnimation(MindMapTitle.OpacityProperty, animationOfChildTitle);

                var animationOfChildLinkPath = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                childTitle.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animationOfChildLinkPath);
            }

            displayCenterTtile.IsSelectedTitle = true;
            this.DisplayTitle = displayCenterTtile;
            this.SelectedTitle = displayCenterTtile;

#if DEBUG
            MainCanvas.Background = Brushes.Red;
#endif
        }

        private void DisplayWithDownOrgView(TreeElement element)
        {
            if (element == null) return;
            if (element.Children.Count <= 0) return;

            HideAndRemoveTitles();

            var parent = element.ParentElement;
            var children = element.Children;

            MindMapTitle parentTitle = null;
            if (parent != null)
            {
                parentTitle = new MindMapTitle(this, element.ParentElement, MindMapTitle.TitleRole.AncestorsTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.AncestorsTitle, };
                parentTitle.IsSelectedChanged += Title_IsSelectedChanged;
                this.MainCanvas.Children.Add(parentTitle);
                parentTitle.InvalidateMeasure();
                parentTitle.UpdateLayout();
            }

            var displayCenterTtile = new MindMapTitle(this, element, MindMapTitle.TitleRole.DisplayCenterTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.DisplayCenterTitle, };
            displayCenterTtile.IsSelectedChanged += Title_IsSelectedChanged;
            this.MainCanvas.Children.Add(displayCenterTtile);
            displayCenterTtile.InvalidateMeasure();
            displayCenterTtile.UpdateLayout();

            this.SelectedTitle = displayCenterTtile;
            displayCenterTtile.IsSelectedTitle = true;

            // 1. 先生成所有标题，放到容器（Canvas）中，这样就可得知所有标题所需要的空间尺寸
            List<MindMapTitle> childTitles = null;
            if (children.Count > 0)
            {
                childTitles = new List<MindMapTitle>();
                foreach (var te in children)
                {
                    var newChildTitle = new MindMapTitle(this, te, MindMapTitle.TitleRole.ChildTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.ChildTitle, };
                    newChildTitle.IsSelectedChanged += Title_IsSelectedChanged;
                    childTitles.Add(newChildTitle);
                    this.MainCanvas.Children.Add(newChildTitle);
                    newChildTitle.InvalidateMeasure();
                    newChildTitle.UpdateLayout();
                }
            }

            // 2. 计算子节点需要的尺寸，这需要考虑布局
            var spacing = baseSpacingTimes * this.TitleDefaultFontSize;

            var width = displayCenterTtile.ActualWidth;
            if (parentTitle != null)
            {
                width = Math.Max(parentTitle.ActualWidth, displayCenterTtile.ActualWidth);
            }
            width += spacing * 2;

            var height = 0.0;
            if (parentTitle != null)
            {
                height += parentTitle.ActualHeight + spacing;
            }

            height += spacing + displayCenterTtile.ActualHeight;

            var childrenWidth = spacing;
            var maxChildHeight = 0.0;
            if (childTitles.Count > 0)
            {
                foreach (var childTitle in childTitles)
                {
                    childrenWidth += childTitle.ActualWidth;
                    childrenWidth += spacing;
                    maxChildHeight = Math.Max(maxChildHeight, childTitle.ActualHeight);
                }
            }

            if (maxChildHeight > 0)
            {
                height += spacing + maxChildHeight;
            }

            height += spacing * childTitles.Count;

            MainCanvas.Width = Math.Max(childrenWidth, width);
            MainCanvas.Height = height;

            var baseHeight = 0.0;

            if (parentTitle != null)
            {
                Canvas.SetTop(parentTitle, parentTitle.Top = spacing);
                Canvas.SetLeft(parentTitle, parentTitle.Left = MainCanvas.Width / 2 - parentTitle.ActualWidth / 2);                     // 左对齐

                // 透明动画
                var animationOfParentTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                parentTitle.BeginAnimation(MindMapTitle.OpacityProperty, animationOfParentTitle);
                baseHeight += parentTitle.ActualHeight + spacing;
            }

            baseHeight += spacing;
            Canvas.SetTop(displayCenterTtile, displayCenterTtile.Top = baseHeight);
            Canvas.SetLeft(displayCenterTtile, displayCenterTtile.Left = MainCanvas.Width / 2 - displayCenterTtile.ActualWidth / 2);
            baseHeight += displayCenterTtile.ActualHeight;

            // 透明动画
            var animationOfDisplayCenterTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
            displayCenterTtile.BeginAnimation(MindMapTitle.OpacityProperty, animationOfDisplayCenterTitle);

            baseHeight += spacing * childTitles.Count;
            var baseWidth = spacing;
            if (childTitles.Count > 0)
            {
                foreach (var childTitle in childTitles)
                {
                    Canvas.SetTop(childTitle, childTitle.Top = baseHeight + (maxChildHeight - childTitle.ActualHeight) / 2);
                    Canvas.SetLeft(childTitle, childTitle.Left = baseWidth);
                    baseWidth += childTitle.ActualWidth + spacing;

                    // 连线
                    childTitle.LinkPointStart = new Point(childTitle.TopCenter.X, childTitle.TopCenter.Y);
                    childTitle.LinkPoint1 = new Point(childTitle.TopCenter.X, displayCenterTtile.BottomCenter.Y + (childTitle.TopCenter.Y - displayCenterTtile.BottomCenter.Y) / 2);
                    childTitle.LinkPoint2 = new Point(displayCenterTtile.BottomCenter.X, childTitle.LinkPoint1.Y);
                    childTitle.LinkPoint3 = displayCenterTtile.BottomCenter;

                    childTitle.RefreshLine(null);
                    Canvas.SetZIndex(childTitle.LinkToParentPath, 1);
                    MainCanvas.Children.Add(childTitle.LinkToParentPath);

                    // 透明动画
                    var animationOfChildTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    childTitle.BeginAnimation(MindMapTitle.OpacityProperty, animationOfChildTitle);

                    var animationOfChildLinkPath = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    childTitle.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animationOfChildLinkPath);
                }
            }

            if (parentTitle != null)
            {
                var pt1 = displayCenterTtile.LinkPointStart = displayCenterTtile.TopCenter;
                var pt2 = displayCenterTtile.LinkPoint3 = parentTitle.BottomCenter;
                displayCenterTtile.LinkPoint1 = new Point(displayCenterTtile.TopCenter.X, pt1.Y + (pt2.Y - pt1.Y) / 2);
                displayCenterTtile.LinkPoint2 = new Point(parentTitle.BottomCenter.X, displayCenterTtile.LinkPoint1.Y);
                displayCenterTtile.RefreshLine(null);
                MainCanvas.Children.Add(displayCenterTtile.LinkToParentPath);

                // 透明动画
                var animationOfToParentLinkPath = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                displayCenterTtile.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animationOfToParentLinkPath);
            }

            displayCenterTtile.IsSelectedTitle = true;
            this.DisplayTitle = displayCenterTtile;
            this.SelectedTitle = displayCenterTtile;

#if DEBUG
            MainCanvas.Background = Brushes.Red;
#endif
        }

        private void DisplayWithUpOrgView(TreeElement element)
        {
            if (element == null) return;
            if (element.Children.Count <= 0) return;

            HideAndRemoveTitles();

            var parent = element.ParentElement;
            var children = element.Children;

            MindMapTitle parentTitle = null;
            if (parent != null)
            {
                parentTitle = new MindMapTitle(this, element.ParentElement, MindMapTitle.TitleRole.AncestorsTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.AncestorsTitle, };
                parentTitle.IsSelectedChanged += Title_IsSelectedChanged;
                this.MainCanvas.Children.Add(parentTitle);
                parentTitle.InvalidateMeasure();
                parentTitle.UpdateLayout();
            }

            var displayCenterTtile = new MindMapTitle(this, element, MindMapTitle.TitleRole.DisplayCenterTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.DisplayCenterTitle, };
            displayCenterTtile.IsSelectedChanged += Title_IsSelectedChanged;
            this.MainCanvas.Children.Add(displayCenterTtile);
            displayCenterTtile.InvalidateMeasure();
            displayCenterTtile.UpdateLayout();

            this.SelectedTitle = displayCenterTtile;
            displayCenterTtile.IsSelectedTitle = true;

            // 1. 先生成所有标题，放到容器（Canvas）中，这样就可得知所有标题所需要的空间尺寸
            List<MindMapTitle> childTitles = null;
            if (children.Count > 0)
            {
                childTitles = new List<MindMapTitle>();
                foreach (var te in children)
                {
                    var newChildTitle = new MindMapTitle(this, te, MindMapTitle.TitleRole.ChildTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.ChildTitle, };
                    newChildTitle.IsSelectedChanged += Title_IsSelectedChanged;
                    childTitles.Add(newChildTitle);
                    this.MainCanvas.Children.Add(newChildTitle);
                    newChildTitle.InvalidateMeasure();
                    newChildTitle.UpdateLayout();
                }
            }

            // 2. 计算子节点需要的尺寸，这需要考虑布局
            var spacing = baseSpacingTimes * this.TitleDefaultFontSize;

            var width = displayCenterTtile.ActualWidth;
            if (parentTitle != null)
            {
                width = Math.Max(parentTitle.ActualWidth, displayCenterTtile.ActualWidth);
            }
            width += spacing * 2;

            var height = 0.0;
            if (parentTitle != null)
            {
                height += parentTitle.ActualHeight + spacing;
            }

            height += spacing + displayCenterTtile.ActualHeight;

            var childrenWidth = spacing;
            var maxChildHeight = 0.0;
            if (childTitles.Count > 0)
            {
                foreach (var childTitle in childTitles)
                {
                    childrenWidth += childTitle.ActualWidth;
                    childrenWidth += spacing;
                    maxChildHeight = Math.Max(maxChildHeight, childTitle.ActualHeight);
                }
            }

            if (maxChildHeight > 0)
            {
                height += spacing + maxChildHeight;
            }

            height += spacing * childTitles.Count;

            MainCanvas.Width = Math.Max(childrenWidth, width);
            MainCanvas.Height = height;

            var baseHeight = 0.0;

            if (parentTitle != null)
            {
                Canvas.SetTop(parentTitle, parentTitle.Top = MainCanvas.Height - spacing - parentTitle.ActualHeight);
                Canvas.SetLeft(parentTitle, parentTitle.Left = MainCanvas.Width / 2 - parentTitle.ActualWidth / 2);                     // 左对齐

                // 透明动画
                var animationOfParentTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                parentTitle.BeginAnimation(MindMapTitle.OpacityProperty, animationOfParentTitle);
                baseHeight += parentTitle.ActualHeight + spacing;
            }

            baseHeight += spacing;
            Canvas.SetTop(displayCenterTtile, displayCenterTtile.Top = MainCanvas.Height - baseHeight - displayCenterTtile.ActualHeight);
            Canvas.SetLeft(displayCenterTtile, displayCenterTtile.Left = MainCanvas.Width / 2 - displayCenterTtile.ActualWidth / 2);
            baseHeight += displayCenterTtile.ActualHeight;

            // 透明动画
            var animationOfDisplayCenterTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
            displayCenterTtile.BeginAnimation(MindMapTitle.OpacityProperty, animationOfDisplayCenterTitle);

            baseHeight += spacing;
            var baseWidth = spacing;
            if (childTitles.Count > 0)
            {
                foreach (var childTitle in childTitles)
                {
                    Canvas.SetTop(childTitle, childTitle.Top = spacing + (maxChildHeight - childTitle.ActualHeight) / 2);
                    Canvas.SetLeft(childTitle, childTitle.Left = baseWidth);
                    baseWidth += childTitle.ActualWidth + spacing;

                    // 连线
                    childTitle.LinkPointStart = new Point(childTitle.BottomCenter.X, childTitle.BottomCenter.Y);
                    childTitle.LinkPoint1 = new Point(childTitle.BottomCenter.X, childTitle.BottomCenter.Y + (displayCenterTtile.TopCenter.Y - childTitle.BottomCenter.Y) / 2);
                    childTitle.LinkPoint2 = new Point(displayCenterTtile.TopCenter.X, childTitle.LinkPoint1.Y);
                    childTitle.LinkPoint3 = displayCenterTtile.TopCenter;

                    childTitle.RefreshLine(null);
                    Canvas.SetZIndex(childTitle.LinkToParentPath, 1);
                    MainCanvas.Children.Add(childTitle.LinkToParentPath);

                    // 透明动画
                    var animationOfChildTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    childTitle.BeginAnimation(MindMapTitle.OpacityProperty, animationOfChildTitle);

                    var animationOfChildLinkPath = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    childTitle.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animationOfChildLinkPath);
                }
            }

            if (parentTitle != null)
            {
                var pt1 = displayCenterTtile.LinkPointStart = displayCenterTtile.BottomCenter;
                var pt2 = displayCenterTtile.LinkPoint3 = parentTitle.TopCenter;
                displayCenterTtile.LinkPoint1 = new Point(displayCenterTtile.BottomCenter.X, pt1.Y + (pt2.Y - pt1.Y) / 2);
                displayCenterTtile.LinkPoint2 = new Point(parentTitle.TopCenter.X, displayCenterTtile.LinkPoint1.Y);
                displayCenterTtile.RefreshLine(null);
                MainCanvas.Children.Add(displayCenterTtile.LinkToParentPath);

                // 透明动画
                var animationOfToParentLinkPath = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                displayCenterTtile.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animationOfToParentLinkPath);
            }

            displayCenterTtile.IsSelectedTitle = true;
            this.DisplayTitle = displayCenterTtile;
            this.SelectedTitle = displayCenterTtile;

#if DEBUG
            MainCanvas.Background = Brushes.Red;
#endif
        }

        private void DisplayWithFishView(TreeElement element, ViewType displayView)
        {
            switch (displayView)
            {
                case ViewType.LeftFish:
                    {
                        DisplayWithLeftFishView(element);
                        break;
                    }
                case ViewType.RightFish:
                    {
                        DisplayWithRightFishView(element);
                        break;
                    }
            }
        }

        /// <summary>
        /// 右向鱼骨图。
        /// </summary>
        /// <param name="element"></param>
        private void DisplayWithRightFishView(TreeElement element)
        {
            if (element == null) return;
            if (element.Children.Count <= 0) return;

            HideAndRemoveTitles();

            var parent = element.ParentElement;
            var children = element.Children;

            // 鱼骨图不显示上级

            // 当前标题为鱼头
            var displayCenterTtile = new MindMapTitle(this, element, MindMapTitle.TitleRole.DisplayCenterTitle)
            {
                Opacity = 0,
                Role = MindMapTitle.TitleRole.DisplayCenterTitle,
                IsRightFishHeaderTitle = true,
            };
            displayCenterTtile.IsSelectedChanged += Title_IsSelectedChanged;
            this.MainCanvas.Children.Add(displayCenterTtile);
            displayCenterTtile.InvalidateMeasure();
            displayCenterTtile.UpdateLayout();

            this.SelectedTitle = displayCenterTtile;
            displayCenterTtile.IsSelectedTitle = true;

            // 1. 先生成所有标题，放到容器（Canvas）中，这样就可得知所有标题所需要的空间尺寸
            if (displayCenterTtile.Children == null)
            {
                displayCenterTtile.Children = new List<MindMapTitle>();
            }

            var istop = true;
            foreach (var te in children)
            {
                var newChildTitle = new MindMapTitle(this, te, MindMapTitle.TitleRole.ChildTitle)
                {
                    ParentTitle = displayCenterTtile,
                    Opacity = 0,
                    Role = MindMapTitle.TitleRole.ChildTitle,
                    IsRightFishBranchTitle = true,
                    IsTopFishBranch = istop,
                };
                newChildTitle.IsSelectedChanged += Title_IsSelectedChanged;
                if (displayCenterTtile.Children == null)
                {
                    displayCenterTtile.Children = new List<MindMapTitle>();
                }
                displayCenterTtile.Children.Add(newChildTitle);
                this.MainCanvas.Children.Add(newChildTitle);
                newChildTitle.InvalidateMeasure();
                newChildTitle.UpdateLayout();

                // 鱼骨图不显示当前标题的父级，但显示当前标题、当前标题的子级和孙级
                foreach (var tte in te.Children)
                {
                    var newSubChildTitle = new MindMapTitle(this, tte, MindMapTitle.TitleRole.ChildTitle)
                    {
                        Opacity = 0,
                        Role = MindMapTitle.TitleRole.ChildTitle,
                        ParentTitle = newChildTitle,
                        IsTopFishBranch = istop,
                    };
                    newSubChildTitle.IsSelectedChanged += Title_IsSelectedChanged;
                    if (newChildTitle.Children == null)
                    {
                        newChildTitle.Children = new List<MindMapTitle>();
                    }
                    newChildTitle.Children.Add(newSubChildTitle);
                    this.MainCanvas.Children.Add(newSubChildTitle);
                    newSubChildTitle.InvalidateArrange();
                    newSubChildTitle.UpdateLayout();
                }

                istop = !istop;
            }

            // 2. 计算子节点需要的尺寸，这需要考虑布局
            var spacing = baseSpacingTimes * this.TitleDefaultFontSize / 2;

            // 计算鱼骨图的尺寸，基本思路是先把它当成交叉垂直图来计算，然后再通过修改横坐标来实现倾斜
            // 孙级标题的倾斜：上侧按孙级标题左上角倾斜，下侧按左下角倾斜
            // 子级标题的倾斜：上侧按左下角

            // 宽度= spacing + disTitle.Width + spacing + branch....width + spacing + tail.width + spacing + 所有分支最右侧的横坐标与最右分支最右点的差值
            // 鱼骨图的宽度由上、下两侧的宽度结合所有分支倾斜后的最右侧坐标决定。

            // 这两个位置用于辅助确定鱼尾的位置和全图的最终宽度。
            // 由于分支是倾斜的，当分支的子级（当前的孙级）太多时，会向右倾斜到超出鱼尾的位置，导致全图必须变得更宽
            //var maxBranchOffsetOfTopRight = 0.0;
            //var maxBranchOffsetOfBottomRight = 0.0;

            // 这两个变量用来计算上侧分支、下侧分支的总宽度，并加上鱼头、鱼尾、两侧边距，再结合倾斜角度带来的上、下侧最右坐标，即可确定全图的宽度。
            var topWidth = 0.0;
            var bottomWidth = 0.0;

            // 它们的值由每个分支中所有孙级标题的高度和间距决定。最终用以决定全图的高度（全图高度还需要比较鱼头的高度）。
            var maxTopHeight = 0.0;
            var maxBottomHeight = 0.0;

            var fishHeaderWidth = Math.Max(spacing * 4, displayCenterTtile.ActualWidth);
            var fishHeaderHeight = Math.Max(spacing * 4, displayCenterTtile.ActualHeight);

            var fishTailWidth = Math.Max(spacing * 4, displayCenterTtile.ActualWidth);  // 鱼头不宜固定
            var fishTailHeight = fishTailWidth;  // 搞个扇形即可

            // 这两个变量仅用于计算上下侧分支的总宽度，不包含两侧边、鱼头、鱼尾等
            var topBranchesWidth = 0.0;
            var bottomBranchesWidth = 0.0;

            var maxTopBranchRight = spacing;
            var maxBottomBranchRight = spacing * 2;   // 下侧错开一个 spacing

            for (int i = 0; i < displayCenterTtile.Children.Count; i++)
            {
                var branchTitle = displayCenterTtile.Children[i];
                var maxChildWidth = 0.0;
                var childrenHeight = 0.0;
                if (branchTitle.Children != null)
                {
                    foreach (var child in branchTitle.Children)
                    {
                        maxChildWidth = Math.Max(child.ActualWidth, maxChildWidth);
                        childrenHeight += spacing + child.ActualHeight;
                    }
                }
                else
                {
                    maxChildWidth = spacing;
                    childrenHeight = branchTitle.ActualWidth / Math.Sqrt(3) + branchTitle.ActualHeight / 2;
                }

                var branchTitleHeight = branchTitle.ActualWidth / 2 * Math.Sqrt(3) + branchTitle.ActualHeight / 2;
                var branchHeight = Math.Max(branchTitleHeight, childrenHeight);

                if (i % 2 == 0)
                {
                    topBranchesWidth += branchTitle.ActualHeight + TitleDefaultFontSize * 2 + maxChildWidth + spacing;
                    maxTopBranchRight += childrenHeight / Math.Sqrt(3);
                    maxTopHeight = Math.Max(maxTopHeight, branchHeight);
                }
                else
                {
                    bottomBranchesWidth += branchTitle.ActualHeight + TitleDefaultFontSize * 2 + maxChildWidth + spacing;
                    maxBottomBranchRight += childrenHeight / Math.Sqrt(3);
                    maxBottomHeight = Math.Max(maxBottomHeight, branchHeight);
                }
            }

            topWidth = spacing + fishHeaderWidth + spacing + topBranchesWidth + fishTailWidth + spacing;         // 左侧的 spacing 需要再加
            bottomWidth = spacing + fishHeaderWidth + spacing + bottomBranchesWidth + fishTailWidth + spacing;   // 左侧的 spacing 需要再加

            var w = MainCanvas.Width = Math.Max(topWidth, bottomWidth) + spacing * 3;  // 多加3个spacing，防止出界
            maxTopHeight = Math.Max(maxTopHeight, Math.Max(displayCenterTtile.ActualHeight, displayCenterTtile.ActualWidth));
            maxBottomHeight = Math.Max(maxBottomHeight, Math.Max(displayCenterTtile.ActualHeight, displayCenterTtile.ActualWidth));
            MainCanvas.Height = spacing + maxTopHeight + maxBottomHeight + spacing;

            var baseHeight = spacing + maxTopHeight;
            Canvas.SetLeft(displayCenterTtile, displayCenterTtile.Left = (w - spacing * 2 - displayCenterTtile.ActualWidth));
            Canvas.SetTop(displayCenterTtile, displayCenterTtile.Top = (baseHeight - displayCenterTtile.ActualHeight / 2));
            var animaOfDisplayCenterTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
            displayCenterTtile.BeginAnimation(Path.OpacityProperty, animaOfDisplayCenterTitle);
            //displayCenterTtile.Opacity = 1;

            var topBranchStartX = w - (fishHeaderWidth + spacing * 4);
            var bottomBranchStartX = w - (fishHeaderWidth + spacing * 6);  // 无论是奇数个还是偶数个，反正下侧向左偏点没错。

            // 布局子级、孙级 
            for (int i = 0; i < displayCenterTtile.Children.Count; i++)
            {
                var branchTitle = displayCenterTtile.Children[i];

                if (i % 2 == 0)
                {
                    Canvas.SetLeft(branchTitle, branchTitle.Left = (topBranchStartX - branchTitle.ActualWidth));
                    Canvas.SetTop(branchTitle, branchTitle.Top = baseHeight - branchTitle.ActualHeight);

                    // 再加旋转
                    branchTitle.RenderTransform = new RotateTransform(60, branchTitle.ActualWidth, branchTitle.ActualHeight);       // 旋转是按矩形坐标系来的
                    branchTitle.IsThemeTitle = true;
                    var animaOfBranchTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    branchTitle.BeginAnimation(Path.OpacityProperty, animaOfBranchTitle);
                    //branchTitle.Opacity = 1;

                    var maxWidthOfChildren = 0.0;
                    var baseBranchChildHeight = baseHeight - spacing;
                    Point branchLineEndPoint = new Point(branchTitle.RightCenter.X - branchTitle.ActualWidth / 2, baseHeight - branchTitle.ActualWidth / 2 * Math.Sqrt(3));

                    if (branchTitle.Children != null)
                    {
                        foreach (var child in branchTitle.Children)
                        {
                            Canvas.SetTop(child, child.Top = baseBranchChildHeight - child.ActualHeight);
                            Canvas.SetLeft(child, child.Left = (topBranchStartX - Math.Abs(child.Top - baseHeight) / Math.Sqrt(3)) - child.ActualWidth);
                            var animaOfChildTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                            child.BeginAnimation(Path.OpacityProperty, animaOfChildTitle);
                            //child.Opacity = 1;

                            maxWidthOfChildren = Math.Max(maxWidthOfChildren, child.ActualWidth);
                            baseBranchChildHeight -= (child.ActualHeight + spacing);
                            branchLineEndPoint = child.TopRight;
                        }
                    }
                    else
                    {
                        maxWidthOfChildren = spacing;
                    }

                    branchTitle.LinkPoint3 = branchLineEndPoint;
                    branchTitle.LinkPointStart = new Point(branchLineEndPoint.X + Math.Abs(branchLineEndPoint.Y - baseHeight) / Math.Sqrt(3), baseHeight);
                    branchTitle.LinkPoint1 = branchTitle.LinkPoint2 = branchTitle.GetLinkCenterPoint();
                    branchTitle.RefreshLine(null);
                    MainCanvas.Children.Add(branchTitle.LinkToParentPath);
                    var animaOfChildTitleLinkPath = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    branchTitle.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animaOfChildTitleLinkPath);
                    //branchTitle.LinkToParentPath.Opacity = 1;

                    topBranchStartX -= branchTitle.ActualHeight + maxWidthOfChildren + spacing * 2;
                }
                else
                {
                    Canvas.SetLeft(branchTitle, branchTitle.Left = bottomBranchStartX - branchTitle.ActualWidth);
                    Canvas.SetTop(branchTitle, branchTitle.Top = baseHeight);

                    // 再加旋转
                    branchTitle.RenderTransform = new RotateTransform(-60, branchTitle.ActualWidth, 0);
                    branchTitle.IsThemeTitle = true;
                    var animaOfBranchTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    branchTitle.BeginAnimation(Path.OpacityProperty, animaOfBranchTitle);
                    //branchTitle.Opacity = 1;

                    var maxWidthOfChildren = 0.0;
                    var baseBranchChildHeight = baseHeight + spacing;
                    Point branchLineEndPoint = new Point(branchTitle.RightCenter.X - branchTitle.ActualWidth / 2, baseHeight + branchTitle.ActualWidth / 2 * Math.Sqrt(3)); ;

                    if (branchTitle.Children != null)
                    {
                        foreach (var child in branchTitle.Children)
                        {
                            Canvas.SetTop(child, child.Top = baseBranchChildHeight);
                            Canvas.SetLeft(child, child.Left = (bottomBranchStartX - Math.Abs(child.BottomCenter.Y - baseHeight) / Math.Sqrt(3)) - child.ActualWidth);
                            var animaOfChildTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                            child.BeginAnimation(Path.OpacityProperty, animaOfChildTitle);
                            //child.Opacity = 1;

                            maxWidthOfChildren = Math.Max(maxWidthOfChildren, child.ActualWidth);
                            baseBranchChildHeight += (child.ActualHeight + spacing);
                            branchLineEndPoint = child.BottomRight;
                        }
                    }
                    else
                    {
                        maxWidthOfChildren = spacing;
                    }

                    branchTitle.LinkPoint3 = branchLineEndPoint;
                    branchTitle.LinkPointStart = new Point(branchLineEndPoint.X + Math.Abs(branchLineEndPoint.Y - baseHeight) / Math.Sqrt(3), baseHeight);
                    branchTitle.LinkPoint1 = branchTitle.LinkPoint2 = branchTitle.GetLinkCenterPoint();
                    branchTitle.RefreshLine(null);
                    MainCanvas.Children.Add(branchTitle.LinkToParentPath);
                    var animaOfChildTitleLinkPath = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    branchTitle.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animaOfChildTitleLinkPath);
                    //branchTitle.LinkToParentPath.Opacity = 1;

                    bottomBranchStartX -= branchTitle.ActualHeight + maxWidthOfChildren + spacing * 2;
                }
            }

            var fishTailStartX = Math.Min(topBranchStartX, bottomBranchStartX);

            displayCenterTtile.LinkPointStart = displayCenterTtile.LeftCenter;
            displayCenterTtile.LinkPoint3 = new Point(fishTailStartX, displayCenterTtile.RightCenter.Y);
            displayCenterTtile.LinkPoint1 = new Point(fishTailStartX + (displayCenterTtile.LeftCenter.X - fishTailStartX) / 2, displayCenterTtile.LeftCenter.Y);
            displayCenterTtile.LinkPoint2 = displayCenterTtile.LinkPoint1;
            displayCenterTtile.RefreshLine(null);
            MainCanvas.Children.Add(displayCenterTtile.LinkToParentPath);
            displayCenterTtile.LinkToParentPath.Opacity = 1;

            // WPF 画鱼尾线：Path.Data -> Geometry.Figures -> Figure.Segments -> Segment
            var tailPath = new Path()
            {
                Opacity = 0,
                Stroke = displayCenterTtile.LinkToParentPath.Stroke,
                StrokeThickness = displayCenterTtile.LinkToParentPath.StrokeThickness,
                Fill = TitleDefaultBackground,
                StrokeLineJoin = PenLineJoin.Round,
            };
            var tailGeometry = new PathGeometry();
            var tailPathFigure = new PathFigure() { StartPoint = displayCenterTtile.LinkPoint3, IsClosed = true, };

            var lineSeg1 = new LineSegment() { Point = new Point(displayCenterTtile.LinkPoint3.X - fishTailWidth / Math.Sqrt(2), displayCenterTtile.LinkPoint3.Y - fishTailWidth / Math.Sqrt(2)), };
            var arcSeg = new ArcSegment(new Point(lineSeg1.Point.X, baseHeight + (baseHeight - lineSeg1.Point.Y)),
                                        new Size(fishTailWidth, fishTailWidth), -90, false, SweepDirection.Counterclockwise, true);
            var lineSeg2 = new LineSegment() { Point = tailPathFigure.StartPoint };

            tailPathFigure.Segments.Add(lineSeg1);
            tailPathFigure.Segments.Add(arcSeg);
            tailPathFigure.Segments.Add(lineSeg2);

            tailGeometry.Figures.Add(tailPathFigure);
            tailPath.Data = tailGeometry;
            MainCanvas.Children.Add(tailPath);
            tailPath.Opacity = 1;

            // WPF 画鱼头线：Path.Data -> Geometry.Figures -> Figure.Segments -> Segment
            var headerPath = new Path()
            {
                Opacity = 0,
                Stroke = displayCenterTtile.LinkToParentPath.Stroke,
                StrokeThickness = displayCenterTtile.LinkToParentPath.StrokeThickness,
                Fill = TitleDefaultBackground,
                StrokeLineJoin = PenLineJoin.Round,
            };
            var headerGeometry = new PathGeometry();
            var headerPathFigure = new PathFigure() { StartPoint = new Point(displayCenterTtile.LeftCenter.X, baseHeight - displayCenterTtile.ActualWidth), IsClosed = true, };

            var lineSeg3 = new LineSegment() { Point = new Point(displayCenterTtile.LeftCenter.X, baseHeight + displayCenterTtile.ActualWidth) };
            var arcSeg2 = new ArcSegment(headerPathFigure.StartPoint, new Size(displayCenterTtile.ActualWidth, displayCenterTtile.ActualWidth), 180, true, SweepDirection.Counterclockwise, true);

            headerPathFigure.Segments.Add(lineSeg3);
            headerPathFigure.Segments.Add(arcSeg2);

            headerGeometry.Figures.Add(headerPathFigure);
            headerPath.Data = headerGeometry;
            MainCanvas.Children.Add(headerPath);
            headerPath.Opacity = 1;
            Canvas.SetZIndex(headerPath, -1);

            displayCenterTtile.IsSelectedTitle = true;
            this.DisplayTitle = displayCenterTtile;
            this.SelectedTitle = displayCenterTtile;

#if DEBUG
            MainCanvas.Background = Brushes.Bisque;
#endif
        }

        /// <summary>
        /// 左向鱼骨图。
        /// </summary>
        /// <param name="element"></param>
        private void DisplayWithLeftFishView(TreeElement element)
        {
            if (element == null) return;
            if (element.Children.Count <= 0) return;

            HideAndRemoveTitles();

            var parent = element.ParentElement;
            var children = element.Children;

            // 鱼骨图不显示上级

            // 当前标题为鱼头
            var displayCenterTtile = new MindMapTitle(this, element, MindMapTitle.TitleRole.DisplayCenterTitle)
            {
                Opacity = 0,
                Role = MindMapTitle.TitleRole.DisplayCenterTitle,
                IsLeftFishHeaderTitle = true,
            };
            displayCenterTtile.IsSelectedChanged += Title_IsSelectedChanged;
            this.MainCanvas.Children.Add(displayCenterTtile);
            displayCenterTtile.InvalidateMeasure();
            displayCenterTtile.UpdateLayout();

            this.SelectedTitle = displayCenterTtile;
            displayCenterTtile.IsSelectedTitle = true;

            // 1. 先生成所有标题，放到容器（Canvas）中，这样就可得知所有标题所需要的空间尺寸
            if (displayCenterTtile.Children == null)
            {
                displayCenterTtile.Children = new List<MindMapTitle>();
            }

            var istop = true;
            foreach (var te in children)
            {
                var newChildTitle = new MindMapTitle(this, te, MindMapTitle.TitleRole.ChildTitle)
                {
                    Opacity = 0,
                    Role = MindMapTitle.TitleRole.ChildTitle,
                    IsLeftFishBranchTitle = true,
                    ParentTitle = displayCenterTtile,
                    IsTopFishBranch = istop,
                };

                newChildTitle.IsSelectedChanged += Title_IsSelectedChanged;
                if (displayCenterTtile.Children == null)
                {
                    displayCenterTtile.Children = new List<MindMapTitle>();
                }
                displayCenterTtile.Children.Add(newChildTitle);
                this.MainCanvas.Children.Add(newChildTitle);
                newChildTitle.InvalidateMeasure();
                newChildTitle.UpdateLayout();

                // 鱼骨图不显示当前标题的父级，但显示当前标题、当前标题的子级和孙级
                foreach (var tte in te.Children)
                {
                    var newSubChildTitle = new MindMapTitle(this, tte, MindMapTitle.TitleRole.ChildTitle)
                    {
                        Opacity = 0,
                        Role = MindMapTitle.TitleRole.ChildTitle,
                        ParentTitle = newChildTitle,
                        IsTopFishBranch = istop,
                    };
                    newSubChildTitle.IsSelectedChanged += Title_IsSelectedChanged;
                    if (newChildTitle.Children == null)
                    {
                        newChildTitle.Children = new List<MindMapTitle>();
                    }
                    newChildTitle.Children.Add(newSubChildTitle);
                    this.MainCanvas.Children.Add(newSubChildTitle);
                    newSubChildTitle.InvalidateArrange();
                    newSubChildTitle.UpdateLayout();
                }

                istop = !istop;
            }

            // 2. 计算子节点需要的尺寸，这需要考虑布局
            var spacing = baseSpacingTimes * this.TitleDefaultFontSize / 2;

            // 计算鱼骨图的尺寸，基本思路是先把它当成交叉垂直图来计算，然后再通过修改横坐标来实现倾斜
            // 孙级标题的倾斜：上侧按孙级标题左上角倾斜，下侧按左下角倾斜
            // 子级标题的倾斜：上侧按左下角

            // 宽度= spacing + disTitle.Width + spacing + branch....width + spacing + tail.width + spacing + 所有分支最右侧的横坐标与最右分支最右点的差值
            // 鱼骨图的宽度由上、下两侧的宽度结合所有分支倾斜后的最右侧坐标决定。

            // 这两个位置用于辅助确定鱼尾的位置和全图的最终宽度。
            // 由于分支是倾斜的，当分支的子级（当前的孙级）太多时，会向右倾斜到超出鱼尾的位置，导致全图必须变得更宽
            //var maxBranchOffsetOfTopRight = 0.0;
            //var maxBranchOffsetOfBottomRight = 0.0;

            // 这两个变量用来计算上侧分支、下侧分支的总宽度，并加上鱼头、鱼尾、两侧边距，再结合倾斜角度带来的上、下侧最右坐标，即可确定全图的宽度。
            var topWidth = 0.0;
            var bottomWidth = 0.0;

            // 它们的值由每个分支中所有孙级标题的高度和间距决定。最终用以决定全图的高度（全图高度还需要比较鱼头的高度）。
            var maxTopHeight = 0.0;
            var maxBottomHeight = 0.0;

            var fishHeaderWidth = Math.Max(spacing * 4, displayCenterTtile.ActualWidth);
            var fishHeaderHeight = Math.Max(spacing * 4, displayCenterTtile.ActualHeight);

            var fishTailWidth = Math.Max(spacing * 4, displayCenterTtile.ActualWidth);  // 鱼头不宜固定
            var fishTailHeight = fishTailWidth;  // 搞个扇形即可

            // 这两个变量仅用于计算上下侧分支的总宽度，不包含两侧边、鱼头、鱼尾等
            var topBranchesWidth = 0.0;
            var bottomBranchesWidth = 0.0;

            var maxTopBranchRight = spacing;
            var maxBottomBranchRight = spacing * 2;   // 下侧错开一个 spacing

            for (int i = 0; i < displayCenterTtile.Children.Count; i++)
            {
                var branchTitle = displayCenterTtile.Children[i];
                var maxChildWidth = 0.0;
                var childrenHeight = 0.0;
                if (branchTitle.Children != null)
                {
                    foreach (var child in branchTitle.Children)
                    {
                        maxChildWidth = Math.Max(child.ActualWidth, maxChildWidth);
                        childrenHeight += spacing + child.ActualHeight;
                    }
                }
                else
                {
                    maxChildWidth = spacing;
                    childrenHeight = branchTitle.ActualWidth / Math.Sqrt(3) + branchTitle.ActualHeight / 2;
                }

                var branchTitleHeight = branchTitle.ActualWidth / 2 * Math.Sqrt(3) + branchTitle.ActualHeight / 2;
                var branchHeight = Math.Max(branchTitleHeight, childrenHeight);

                if (i % 2 == 0)
                {
                    topBranchesWidth += branchTitle.ActualHeight + TitleDefaultFontSize * 2 + maxChildWidth + spacing;
                    maxTopBranchRight += childrenHeight / Math.Sqrt(3);
                    maxTopHeight = Math.Max(maxTopHeight, branchHeight);
                }
                else
                {
                    bottomBranchesWidth += branchTitle.ActualHeight + TitleDefaultFontSize * 2 + maxChildWidth + spacing;
                    maxBottomBranchRight += childrenHeight / Math.Sqrt(3);
                    maxBottomHeight = Math.Max(maxBottomHeight, branchHeight);
                }
            }

            topWidth = spacing + fishHeaderWidth + spacing + topBranchesWidth + fishTailWidth + spacing;      // 右侧的 spaceing 需要加
            bottomWidth = spacing + fishHeaderWidth + spacing + bottomBranchesWidth + fishTailWidth + spacing;   // 右侧的 spacing 需要加

            maxTopHeight = Math.Max(maxTopHeight, Math.Max(displayCenterTtile.ActualHeight, displayCenterTtile.ActualWidth));
            maxBottomHeight = Math.Max(maxBottomHeight, Math.Max(displayCenterTtile.ActualHeight, displayCenterTtile.ActualWidth));

            var baseHeight = spacing + maxTopHeight;
            Canvas.SetLeft(displayCenterTtile, displayCenterTtile.Left = spacing * 2);
            Canvas.SetTop(displayCenterTtile, displayCenterTtile.Top = (baseHeight - displayCenterTtile.ActualHeight / 2));
            var animaOfDisplayCenterTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
            displayCenterTtile.BeginAnimation(Path.OpacityProperty, animaOfDisplayCenterTitle);
            //displayCenterTtile.Opacity = 1;

            MainCanvas.Width = Math.Max(topWidth, bottomWidth) + spacing * 3;  // 多加3个spacing，防止出界
            MainCanvas.Height = spacing + maxTopHeight + maxBottomHeight + spacing;

            var topBranchStartX = spacing + fishHeaderWidth + spacing * 3;
            var bottomBranchStartX = spacing + fishHeaderWidth + spacing * 5;  // 无论是奇数个还是偶数个，反正下侧向右偏点没错。

            // 布局子级、孙级 
            for (int i = 0; i < displayCenterTtile.Children.Count; i++)
            {
                var branchTitle = displayCenterTtile.Children[i];

                if (i % 2 == 0)
                {
                    Canvas.SetLeft(branchTitle, branchTitle.Left = topBranchStartX);
                    Canvas.SetTop(branchTitle, branchTitle.Top = baseHeight - branchTitle.ActualHeight);

                    // 再加旋转
                    branchTitle.RenderTransform = new RotateTransform(-60, 0, branchTitle.ActualHeight);       // 旋转是按矩形坐标系来的
                    branchTitle.IsThemeTitle = true;
                    var animaOfBranchTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    branchTitle.BeginAnimation(Path.OpacityProperty, animaOfBranchTitle);
                    //branchTitle.Opacity = 1;

                    var maxWidthOfChildren = 0.0;
                    var baseBranchChildHeight = baseHeight - spacing;
                    Point branchLineEndPoint = new Point(branchTitle.Left + branchTitle.ActualWidth / 2, baseHeight - branchTitle.ActualWidth / 2 * Math.Sqrt(3));

                    if (branchTitle.Children != null)
                    {
                        foreach (var child in branchTitle.Children)
                        {
                            Canvas.SetTop(child, child.Top = baseBranchChildHeight - child.ActualHeight);
                            Canvas.SetLeft(child, child.Left = topBranchStartX + Math.Abs(child.Top - baseHeight) / Math.Sqrt(3));
                            var animaOfChildTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                            child.BeginAnimation(Path.OpacityProperty, animaOfChildTitle);
                            //child.Opacity = 1;

                            maxWidthOfChildren = Math.Max(maxWidthOfChildren, child.ActualWidth);
                            baseBranchChildHeight -= (child.ActualHeight + spacing);
                            branchLineEndPoint = child.TopLeft;
                        }
                    }
                    else
                    {
                        maxWidthOfChildren = spacing;
                    }

                    branchTitle.LinkPoint3 = branchLineEndPoint;
                    branchTitle.LinkPointStart = new Point(branchLineEndPoint.X - Math.Abs(branchLineEndPoint.Y - baseHeight) / Math.Sqrt(3), baseHeight);
                    branchTitle.LinkPoint1 = branchTitle.LinkPoint2 = branchTitle.GetLinkCenterPoint();
                    branchTitle.RefreshLine(null);
                    MainCanvas.Children.Add(branchTitle.LinkToParentPath);
                    var animaOfChildTitleLinkPath = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    branchTitle.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animaOfChildTitleLinkPath);
                    //branchTitle.LinkToParentPath.Opacity = 1;

                    topBranchStartX += branchTitle.ActualHeight + maxWidthOfChildren + spacing * 2;
                }
                else
                {
                    Canvas.SetLeft(branchTitle, branchTitle.Left = bottomBranchStartX);
                    Canvas.SetTop(branchTitle, branchTitle.Top = baseHeight);

                    // 再加旋转
                    branchTitle.RenderTransform = new RotateTransform(60, 0, 0);
                    branchTitle.IsThemeTitle = true;
                    var animaOfBranchTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    branchTitle.BeginAnimation(Path.OpacityProperty, animaOfBranchTitle);
                    //branchTitle.Opacity = 1;

                    var maxWidthOfChildren = 0.0;
                    var baseBranchChildHeight = baseHeight + spacing;
                    Point branchLineEndPoint = new Point(branchTitle.Left + branchTitle.ActualWidth / 2, baseHeight + branchTitle.ActualWidth / 2 * Math.Sqrt(3)); ;

                    if (branchTitle.Children != null)
                    {
                        foreach (var child in branchTitle.Children)
                        {
                            Canvas.SetTop(child, child.Top = baseBranchChildHeight);
                            Canvas.SetLeft(child, child.Left = bottomBranchStartX + Math.Abs(child.BottomCenter.Y - baseHeight) / Math.Sqrt(3));
                            var animaOfChildTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                            child.BeginAnimation(Path.OpacityProperty, animaOfChildTitle);
                            //child.Opacity = 1;

                            maxWidthOfChildren = Math.Max(maxWidthOfChildren, child.ActualWidth);
                            baseBranchChildHeight += (child.ActualHeight + spacing);
                            branchLineEndPoint = child.BottomLeft;
                        }
                    }
                    else
                    {
                        maxWidthOfChildren = spacing;
                    }

                    branchTitle.LinkPoint3 = branchLineEndPoint;
                    branchTitle.LinkPointStart = new Point(branchLineEndPoint.X - Math.Abs(branchLineEndPoint.Y - baseHeight) / Math.Sqrt(3), baseHeight);
                    branchTitle.LinkPoint1 = branchTitle.LinkPoint2 = branchTitle.GetLinkCenterPoint();
                    branchTitle.RefreshLine(null);
                    MainCanvas.Children.Add(branchTitle.LinkToParentPath);
                    var animaOfChildTitleLinkPath = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    branchTitle.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animaOfChildTitleLinkPath);
                    //branchTitle.LinkToParentPath.Opacity = 1;

                    bottomBranchStartX += branchTitle.ActualHeight + maxWidthOfChildren + spacing * 2;
                }
            }

            var fishTailStartX = Math.Max(topBranchStartX, bottomBranchStartX);

            displayCenterTtile.LinkPointStart = displayCenterTtile.RightCenter;
            displayCenterTtile.LinkPoint3 = new Point(fishTailStartX, displayCenterTtile.RightCenter.Y);
            displayCenterTtile.LinkPoint1 = new Point(displayCenterTtile.RightCenter.X + (fishTailStartX - displayCenterTtile.RightCenter.X), displayCenterTtile.RightCenter.Y);
            displayCenterTtile.LinkPoint2 = displayCenterTtile.LinkPoint1;
            displayCenterTtile.RefreshLine(null);
            MainCanvas.Children.Add(displayCenterTtile.LinkToParentPath);
            displayCenterTtile.LinkToParentPath.Opacity = 1;

            // WPF 画鱼尾线：Path.Data -> Geometry.Figures -> Figure.Segments -> Segment
            var tailPath = new Path()
            {
                Opacity = 0,
                Stroke = displayCenterTtile.LinkToParentPath.Stroke,
                StrokeThickness = displayCenterTtile.LinkToParentPath.StrokeThickness,
                Fill = TitleDefaultBackground,
                StrokeLineJoin = PenLineJoin.Round,
            };
            var tailGeometry = new PathGeometry();
            var tailPathFigure = new PathFigure() { StartPoint = displayCenterTtile.LinkPoint3, IsClosed = true, };

            var lineSeg1 = new LineSegment() { Point = new Point(displayCenterTtile.LinkPoint3.X + fishTailWidth / Math.Sqrt(2), displayCenterTtile.LinkPoint3.Y - fishTailWidth / Math.Sqrt(2)), };
            var arcSeg = new ArcSegment(new Point(lineSeg1.Point.X, baseHeight + (baseHeight - lineSeg1.Point.Y)),
                                        new Size(fishTailWidth, fishTailWidth), 90, false, SweepDirection.Clockwise, true);
            var lineSeg2 = new LineSegment() { Point = tailPathFigure.StartPoint };

            tailPathFigure.Segments.Add(lineSeg1);
            tailPathFigure.Segments.Add(arcSeg);
            tailPathFigure.Segments.Add(lineSeg2);

            tailGeometry.Figures.Add(tailPathFigure);
            tailPath.Data = tailGeometry;
            MainCanvas.Children.Add(tailPath);
            tailPath.Opacity = 1;

            // WPF 画鱼头线：Path.Data -> Geometry.Figures -> Figure.Segments -> Segment
            var headerPath = new Path()
            {
                Opacity = 0,
                Stroke = displayCenterTtile.LinkToParentPath.Stroke,
                StrokeThickness = displayCenterTtile.LinkToParentPath.StrokeThickness,
                Fill = TitleDefaultBackground,
                StrokeLineJoin = PenLineJoin.Round,
            };
            var headerGeometry = new PathGeometry();
            var headerPathFigure = new PathFigure() { StartPoint = new Point(displayCenterTtile.RightCenter.X, baseHeight - displayCenterTtile.ActualWidth), IsClosed = true, };

            var lineSeg3 = new LineSegment() { Point = new Point(displayCenterTtile.RightCenter.X, baseHeight + displayCenterTtile.ActualWidth) };
            var arcSeg2 = new ArcSegment(headerPathFigure.StartPoint, new Size(displayCenterTtile.ActualWidth, displayCenterTtile.ActualWidth), 180, true, SweepDirection.Clockwise, true);

            headerPathFigure.Segments.Add(lineSeg3);
            headerPathFigure.Segments.Add(arcSeg2);

            headerGeometry.Figures.Add(headerPathFigure);
            headerPath.Data = headerGeometry;
            MainCanvas.Children.Add(headerPath);
            headerPath.Opacity = 1;
            Canvas.SetZIndex(headerPath, -1);

            displayCenterTtile.IsSelectedTitle = true;
            this.DisplayTitle = displayCenterTtile;
            this.SelectedTitle = displayCenterTtile;

#if DEBUG
            MainCanvas.Background = Brushes.Bisque;
#endif
        }

        private void DisplayWithListView(TreeElement element)
        {
            if (element == null) return;
            if (element.Children.Count <= 0) return;

            HideAndRemoveTitles();

            var parent = element.ParentElement;
            var children = element.Children;

            MindMapTitle parentTitle = null;
            if (parent != null)
            {
                parentTitle = new MindMapTitle(this, element.ParentElement, MindMapTitle.TitleRole.AncestorsTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.AncestorsTitle, };
                parentTitle.IsSelectedChanged += Title_IsSelectedChanged;
                this.MainCanvas.Children.Add(parentTitle);
                parentTitle.InvalidateMeasure();
                parentTitle.UpdateLayout();
            }

            var displayCenterTtile = new MindMapTitle(this, element, MindMapTitle.TitleRole.DisplayCenterTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.DisplayCenterTitle, };
            displayCenterTtile.IsSelectedChanged += Title_IsSelectedChanged;
            displayCenterTtile.IsSelectedTitle = true;
            this.MainCanvas.Children.Add(displayCenterTtile);
            displayCenterTtile.InvalidateMeasure();
            displayCenterTtile.UpdateLayout();
            this.SelectedTitle = displayCenterTtile;
            displayCenterTtile.IsSelectedTitle = true;

            // 1. 先生成所有标题，放到容器（Canvas）中，这样就可得知所有标题所需要的空间尺寸
            List<MindMapTitle> childTitles = null;
            if (children.Count > 0)
            {
                childTitles = new List<MindMapTitle>();
                foreach (var te in children)
                {
                    var newChildTitle = new MindMapTitle(this, te, MindMapTitle.TitleRole.ChildTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.ChildTitle, };
                    newChildTitle.IsSelectedChanged += Title_IsSelectedChanged;
                    childTitles.Add(newChildTitle);
                    this.MainCanvas.Children.Add(newChildTitle);
                    newChildTitle.InvalidateMeasure();
                    newChildTitle.UpdateLayout();
                }
            }

            // 2. 计算子节点需要的尺寸，这需要考虑布局
            var spacing = baseSpacingTimes * this.TitleDefaultFontSize;

            var height = 0.0;
            if (parentTitle != null)
            {
                height += spacing;
                Canvas.SetTop(parentTitle, parentTitle.Top = height);
                if (displayCenterTtile.ActualWidth > parentTitle.ActualWidth)
                {
                    Canvas.SetLeft(parentTitle, parentTitle.Left = spacing + (displayCenterTtile.ActualWidth - parentTitle.ActualWidth) / 2);
                }
                else
                {
                    Canvas.SetLeft(parentTitle, parentTitle.Left = spacing);
                }

                height += parentTitle.ActualHeight;

                // 透明动画
                var animationOfParentTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                parentTitle.BeginAnimation(MindMapTitle.OpacityProperty, animationOfParentTitle);
            }

            height += spacing;
            Canvas.SetTop(displayCenterTtile, displayCenterTtile.Top = height);
            if (parentTitle != null)
            {
                if (displayCenterTtile.ActualWidth > parentTitle.ActualWidth)
                {
                    Canvas.SetLeft(displayCenterTtile, displayCenterTtile.Left = spacing);
                }
                else
                {
                    Canvas.SetLeft(displayCenterTtile, displayCenterTtile.Left = spacing + (parentTitle.ActualWidth - displayCenterTtile.ActualWidth) / 2);
                }
            }
            else
            {
                Canvas.SetLeft(displayCenterTtile, displayCenterTtile.Left = spacing);
            }
            height += displayCenterTtile.ActualHeight;

            // 透明动画
            var animationOfDisplayCenterTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
            displayCenterTtile.BeginAnimation(MindMapTitle.OpacityProperty, animationOfDisplayCenterTitle);

            if (parentTitle != null)
            {
                var pt1 = displayCenterTtile.LinkPointStart = displayCenterTtile.TopCenter;
                var pt2 = displayCenterTtile.LinkPoint3 = parentTitle.BottomCenter;
                displayCenterTtile.LinkPoint1 = displayCenterTtile.LinkPoint2 = new Point(pt1.X + (pt2.X - pt1.X) / 2, pt1.Y + (pt2.Y - pt1.Y) / 2);
                displayCenterTtile.RefreshLine(null);
                MainCanvas.Children.Add(displayCenterTtile.LinkToParentPath);

                // 透明动画
                var animationOfToParentLinkPath = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                displayCenterTtile.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animationOfToParentLinkPath);
            }

            var baseIndent = displayCenterTtile.Left + displayCenterTtile.ActualHeight / 2 + spacing + spacing;
            var linkToPoint = new Point(displayCenterTtile.Left + displayCenterTtile.ActualHeight / 2, displayCenterTtile.BottomCenter.Y);

            var maxCanvasWidth = 0.0;
            if (parentTitle != null)
            {
                maxCanvasWidth = spacing + Math.Max(parentTitle.ActualWidth, displayCenterTtile.ActualWidth) + spacing;
            }
            else
            {
                maxCanvasWidth = spacing + displayCenterTtile.ActualWidth + spacing;
            }

            foreach (var childTitle in childTitles)
            {
                height += spacing;   // 每个子级标题顶部应有一个 spacing
                Canvas.SetTop(childTitle, childTitle.Top = height);
                Canvas.SetLeft(childTitle, childTitle.Left = baseIndent);
                Canvas.SetZIndex(childTitle, int.MaxValue);
                height += childTitle.ActualHeight;

                maxCanvasWidth = Math.Max(maxCanvasWidth, baseIndent + childTitle.ActualWidth + spacing);

                childTitle.LinkPointStart = new Point(childTitle.LeftCenter.X - spacing / 2, childTitle.LeftCenter.Y);
                childTitle.LinkPoint1 = new Point(childTitle.Left - spacing * 1.5, childTitle.LeftCenter.Y - spacing);
                childTitle.LinkPoint2 = new Point(childTitle.Left - spacing * 1.5, childTitle.LeftCenter.Y + spacing);
                childTitle.LinkPoint3 = childTitle.LinkPointStart;
                childTitle.LinkToParentPathFigure.IsClosed = true;
                childTitle.LinkToParentPathFigure.IsFilled = true;
                childTitle.LinkToParentPath.Fill = this.TitleDefaultActiveBrush;

                childTitle.RefreshLine(null);
                Canvas.SetZIndex(childTitle.LinkToParentPath, 1);
                MainCanvas.Children.Add(childTitle.LinkToParentPath);

                // 透明动画
                var animationOfChildTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                childTitle.BeginAnimation(MindMapTitle.OpacityProperty, animationOfChildTitle);

                var animationOfChildLinkPath = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                childTitle.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animationOfChildLinkPath);
            }

            MainCanvas.Width = maxCanvasWidth;
            MainCanvas.Height = height + spacing;
            this.DisplayTitle = displayCenterTtile;
            this.SelectedTitle = displayCenterTtile;

#if DEBUG
            MainCanvas.Background = Brushes.Red;
#endif
        }

        private void Title_IsSelectedChanged(object sender, MindMapTitleIsSelectedArgs e)
        {
            // 注意：虽然放在这个事件中，但这些按钮的状态并不是由当前选中的标题决定的，而是由当前正在演示的中心标题决定的！！！
            if (e.IsSelected == false) return;

            if (curDisplayingElement != null && curDisplayingElement.ParentElement == null)
            {
                // 演示一级标题时，全部功能按钮均应可用
                btnFirst.IsEnabled = btnLast.IsEnabled = btnPreview.IsEnabled = btnNext.IsEnabled = true;
                pFirst.Fill = pLast.Fill = Brushes.LightCoral;
                pPreview.Fill = pNext.Fill = Brushes.DeepSkyBlue;

                btnParentElement.IsEnabled = false;
                pParent.Fill = Brushes.Gray;
                return;
            }
            else
            {
                // 截图按钮、保存图像到文件应保留，返回根标题按钮也应保留，其它应禁用
                btnFirst.IsEnabled = btnLast.IsEnabled = btnPreview.IsEnabled = btnNext.IsEnabled = false;
                pFirst.Fill = pLast.Fill = Brushes.Gray;
                pPreview.Fill = pNext.Fill = Brushes.Gray;

                btnParentElement.IsEnabled = true;
                pParent.Fill = Brushes.Firebrick;
            }
        }

        private void DisplayWithAncestorsList(TreeElement element)
        {
            if (element == null) return;

            HideAndRemoveTitles();

            // 1. 找到所有祖先级标题，放到容器（Canvas）中，这样就可得知所有标题所需要的空间尺寸
            List<TreeElement> list = new List<TreeElement>();
            list.Insert(0, element);
            TreeElement preParentElement = element.ParentElement;
            while (preParentElement != null)
            {
                list.Insert(0, preParentElement);
                preParentElement = preParentElement.ParentElement;
            }

            // 2. 计算子节点需要的尺寸，这需要考虑布局
            var spacing = baseSpacingTimes * this.TitleDefaultFontSize;

            var titleList = new List<MindMapTitle>();
            var height = spacing;
            MindMapTitle displayTitle = null;
            foreach (var ele in list)
            {
                var title = new MindMapTitle(this, ele, MindMapTitle.TitleRole.ChildTitle) { Opacity = 0, };
                title.IsSelectedChanged += Title_IsSelectedChanged;
                if (ele == element)
                {
                    title.Role = MindMapTitle.TitleRole.DisplayCenterTitle;
                    this.SelectedTitle = title;
                    title.IsSelectedTitle = true;
                }
                else
                {
                    title.Role = MindMapTitle.TitleRole.AncestorsTitle;
                }
                this.MainCanvas.Children.Add(title);
                titleList.Add(title);
                title.InvalidateMeasure();
                title.UpdateLayout();
                height += title.ActualHeight + spacing;

                displayTitle = title;
            }
            MainCanvas.Height = height;

            // 总高度求出后，最小宽度为总高度的 4/3 倍。
            var baseWidth = height * 4 / 3;
            // 再将总宽度切分，每个分割点都定位一个标题的左侧边位置。
            var splitWidth = baseWidth / (list.Count + 1);

            // 最后再根据所有标题的最右侧位置来判断最终 Canvas 的宽度。
            var left = spacing;
            var top = spacing;
            var maxRight = spacing;
            for (var i = 0; i < titleList.Count; i++)
            {
                var title = titleList[i];
                Canvas.SetTop(title, title.Top = top);
                top += title.ActualHeight + spacing;

                Canvas.SetLeft(title, title.Left = left);
                maxRight = Math.Max(maxRight, left + title.ActualWidth + spacing);    // 这里加的是右边距，所以不用 spalitWidth
                left += splitWidth;

                var animationOfTitle = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                title.BeginAnimation(MindMapTitle.OpacityProperty, animationOfTitle);
            }

            // 下面画连接线
            for (int i = 1; i < titleList.Count; i++)   // 最上层根节点不画线
            {
                var title = titleList[i];

                if (title.Element == null || title.Element.ParentElement == null) continue;

                var parentTitle = titleList[i - 1];
                var pt1 = new Point(parentTitle.Left + parentTitle.ActualHeight / 2, parentTitle.BottomCenter.Y);         // 从上一个节点标题左下角稍右的位置（半圆底点）
                var pt2 = new Point(title.Left + parentTitle.ActualHeight / 2, title.Top);

                var ctrlPt1 = new Point(pt1.X + (pt2.X - pt1.X) / 4, pt2.Y + spacing);
                var ctrlPt2 = new Point(pt2.X - (pt2.X - pt1.X) / 4, pt1.Y - spacing);

                title.LinkPointStart = pt2;
                title.LinkPoint1 = ctrlPt2;
                title.LinkPoint2 = ctrlPt1;
                title.LinkPoint3 = pt1;
                title.RefreshLine(null);

                MainCanvas.Children.Add(title.LinkToParentPath);

                var animationToParent = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                title.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animationToParent);
            }

            MainCanvas.Width = Math.Max(baseWidth, maxRight);
            this.DisplayTitle = displayTitle;
            this.SelectedTitle = displayTitle;

#if DEBUG
            MainCanvas.Background = Brushes.Red;
#endif
        }

        private void HideAndRemoveTitles()
        {
            this.MainCanvas.Children.Clear();
        }

#if DEBUG
        // 仅用于测试布局的圆形
        private Ellipse LayoutEllipse = new Ellipse() { Fill = Brushes.Transparent, Stroke = Brushes.Violet, StrokeThickness = 1, };
#endif

        /// <summary>
        /// 默认视图，显示当前节点、上级节点（如果有）、直接子节点。
        /// </summary>
        /// <param name="element"></param>
        private void DisplayWithDefaultView(TreeElement element)
        {
            if (element == null) return;
            if (element.Children.Count <= 0) return;

            if (element.ParentElement == null)
            {
                // 演示一级标题时，全部功能按钮均应可用
                btnFirst.IsEnabled = btnLast.IsEnabled = btnPreview.IsEnabled = btnNext.IsEnabled = true;
                pFirst.Fill = pLast.Fill = Brushes.LightCoral;
                pPreview.Fill = pNext.Fill = Brushes.DeepSkyBlue;

                btnParentElement.IsEnabled = false;
                pParent.Fill = Brushes.Gray;
            }
            else
            {
                // 截图按钮、保存图像到文件应保留，返回根标题按钮也应保留，其它应禁用
                btnFirst.IsEnabled = btnLast.IsEnabled = btnPreview.IsEnabled = btnNext.IsEnabled = false;
                pFirst.Fill = pLast.Fill = Brushes.Gray;
                pPreview.Fill = pNext.Fill = Brushes.Gray;

                btnParentElement.IsEnabled = true;
                pParent.Fill = Brushes.Firebrick;
            }

            HideAndRemoveTitles();

#if DEBUG
            LayoutEllipse.Visibility = Visibility.Hidden;
            MainCanvas.Children.Add(LayoutEllipse);
#endif

            // 默认视图。只显示当前节点、上级节点、直接子级节点。
            // 上级节点居顶端、当前节点位于中间位置、子级围绕当前节点呈圆形分布，顺序从 1 点种附近到 11 点钟附近。
            // 过程：
            // 1. 先生成所有标题，放到容器（Canvas）中，这样就可得知所有标题所需要的空间尺寸

            var parent = element.ParentElement;
            var children = element.Children;

            MindMapTitle parentTitle = null;
            if (parent != null)
            {
                parentTitle = new MindMapTitle(this, element.ParentElement, MindMapTitle.TitleRole.AncestorsTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.AncestorsTitle, };
                parentTitle.IsSelectedChanged += Title_IsSelectedChanged;
                this.MainCanvas.Children.Add(parentTitle);
                parentTitle.InvalidateMeasure();
                parentTitle.UpdateLayout();
            }

            var displayCenterTtile = new MindMapTitle(this, element, MindMapTitle.TitleRole.DisplayCenterTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.DisplayCenterTitle, };
            displayCenterTtile.IsSelectedChanged += Title_IsSelectedChanged;
            this.MainCanvas.Children.Add(displayCenterTtile);
            displayCenterTtile.InvalidateMeasure();
            displayCenterTtile.UpdateLayout();

            List<MindMapTitle> childTitles = null;
            if (children.Count > 0)
            {
                childTitles = new List<MindMapTitle>();
                foreach (var te in children)
                {
                    var newChildTitle = new MindMapTitle(this, te, MindMapTitle.TitleRole.ChildTitle) { Opacity = 0, Role = MindMapTitle.TitleRole.ChildTitle, };
                    newChildTitle.IsSelectedChanged += Title_IsSelectedChanged;
                    childTitles.Add(newChildTitle);
                    this.MainCanvas.Children.Add(newChildTitle);
                    newChildTitle.InvalidateMeasure();
                    newChildTitle.UpdateLayout();
                }
            }

            // 2. 计算子节点需要的尺寸，这需要考虑布局
            var spacing = baseSpacingTimes * this.TitleDefaultFontSize;

            // Canvas 的高度：topSpacing + parentTitleHeight + n * (titleSpacing + titleHeight) + (titleSpacing + middleBottomTitleHeight) + bottomSpacing
            // 其中，左右两边的各有几个 Title，n * (titleSpacing + titleHeight) 按值更大的那边计算

            var topSpacing = spacing;
            var bottomSpacing = spacing;
            double leftSumHeight = spacing;    // 布局圆中每个标题占一份spacing，还有底部一个spacing
            double rightSumHeight = spacing;
            double middle = childTitles.Count / 2;

            if (childTitles.Count % 2 == 0)
            {
                // 偶数个子级标题，没有底部居中的标题，只需要考虑左右两边即可。
                for (int i = 0; i < childTitles.Count; i++)
                {
                    if (i < middle)
                    {
                        rightSumHeight += spacing + childTitles[i].ActualHeight;
                    }
                    else
                    {
                        leftSumHeight += spacing + childTitles[i].ActualHeight;
                    }
                }

                // 为实现左右两侧垂直居中，左右两侧子节点群需要考虑 offset
                var vOffset = Math.Abs(leftSumHeight - rightSumHeight) / 2;

                var maxHeight = Math.Max(leftSumHeight, rightSumHeight);

                var disSize = Math.Max(displayCenterTtile.ActualHeight, displayCenterTtile.ActualWidth);
                maxHeight = Math.Max(maxHeight, disSize);

                if (maxHeight > leftSumHeight && maxHeight > rightSumHeight)
                {
                    vOffset = (maxHeight - Math.Max(leftSumHeight, rightSumHeight)) / 2;
                }

                MainCanvas.Height = topSpacing + maxHeight + bottomSpacing; // 偶数时没有居中靠底部的子节点。
                if (parentTitle != null)
                {
                    MainCanvas.Height += spacing;
                }

                // 3. 计算左右各个子节点的相对位置（相对于当前中心节点的中心）
                //    从 1 点钟方向→11 点钟方向
                double layoutEllipseDiameter = maxHeight;
                double layoutEllipseRadius = maxHeight / 2;  // 圆半径

                // 如果圆的直径小于 displayTitle.ActualWidth + spacing * 4（即中心标题过宽），提供 expandSpacing
                var expandSpacing = 0.0;
                if (layoutEllipseDiameter < displayCenterTtile.ActualWidth + spacing * 2)
                {
                    expandSpacing = displayCenterTtile.ActualWidth / 2 + spacing - layoutEllipseRadius;
                }

                var centerY = topSpacing + layoutEllipseRadius;
                if (parentTitle != null)
                {
                    centerY += parentTitle.ActualHeight;
                }
                double baseHeight = topSpacing + vOffset;
                if (parentTitle != null)
                {
                    baseHeight += parentTitle.ActualHeight;
                }
                double maxRelativeRightWidth = 0;
                for (int i = 0; i < middle; i++)
                {
                    // 右侧子节点
                    double h = baseHeight + spacing + childTitles[i].ActualHeight / 2;
                    double sh = h - centerY;
                    if (Math.Abs(sh) < 10)
                    {
                        childTitles[i].RelativePointY = 0;
                        childTitles[i].RelativePointX = expandSpacing + layoutEllipseRadius + displayCenterTtile.ActualWidth / 2;  // 右侧节点相对中心的 X 差值总是正的
                    }
                    else
                    {
                        childTitles[i].RelativePointY = sh;          // 右侧节点相对于中心的 Y 差值有正有负
                        childTitles[i].RelativePointX = expandSpacing + Math.Sqrt(layoutEllipseRadius * layoutEllipseRadius - sh * sh) + displayCenterTtile.ActualWidth / 2;  // 右侧节点相对中心的 X 差值总是正的
                    }
                    maxRelativeRightWidth = Math.Max(childTitles[i].RelativePointX + childTitles[i].ActualWidth, maxRelativeRightWidth);
                    baseHeight += spacing + childTitles[i].ActualHeight;
                }

                // 因为是顺时针方向，左侧节点在逆序
                baseHeight = topSpacing + vOffset;
                if (parentTitle != null)
                {
                    baseHeight += parentTitle.ActualHeight;
                }
                double maxRelativeLeftWidth = 0;
                for (int i = childTitles.Count - 1; i >= middle; i--)
                {
                    // 左侧节点
                    double h = baseHeight + spacing + childTitles[i].ActualHeight / 2;
                    double sh = h - centerY;
                    if (Math.Abs(sh) < 10)
                    {
                        childTitles[i].RelativePointY = 0;
                        childTitles[i].RelativePointX = -expandSpacing + -layoutEllipseRadius - displayCenterTtile.ActualWidth / 2;  // 左侧节点相对中心的 X 差值总是负的
                    }
                    else
                    {
                        childTitles[i].RelativePointY = sh;          // 右侧节点相对于中心的 Y 差值有正有负
                        childTitles[i].RelativePointX = -expandSpacing + -Math.Sqrt(layoutEllipseRadius * layoutEllipseRadius - sh * sh) - displayCenterTtile.ActualWidth / 2;  // 左侧节点相对中心的 X 差值总是负的
                    }
                    maxRelativeLeftWidth = Math.Max(-childTitles[i].RelativePointX + childTitles[i].ActualWidth, maxRelativeLeftWidth);
                    baseHeight += spacing + childTitles[i].ActualHeight;
                }

                // 4. 计算出 MainCanvas 需要的宽度
                if (displayCenterTtile.ActualWidth > (maxHeight + spacing * 2))
                {
                    // 如果中心标题很宽（超过了布局圆的直径）
                    // maxHeight 是布局圆的直径，因为已经加在 maxRelativeLeftWidth 和 maxRelativeRightWidth 中，不能重复加。
                    MainCanvas.Width = spacing + maxRelativeLeftWidth + spacing + (displayCenterTtile.ActualWidth - maxHeight) + spacing + maxRelativeRightWidth + spacing;    // 还要考虑中心标题的宽度
                }
                else
                {
                    // maxHeight 是布局圆的直径，因为已经加在 maxRelativeLeftWidth 和 maxRelativeRightWidth 中，不能重复加。
                    MainCanvas.Width = spacing + maxRelativeLeftWidth + maxRelativeRightWidth + spacing;    // 还要考虑中心标题的宽度
                }

                var displayCenterX = spacing + maxRelativeLeftWidth;
                var displayCenterY = spacing + layoutEllipseRadius;
                if (parentTitle != null)
                {
                    displayCenterY += parentTitle.ActualHeight;
                }

                // 5. 根据 MainCanvas 的宽、高，计算出当前中心节点的中心位置和中心节点标题的左上角坐标

                Canvas.SetLeft(displayCenterTtile, displayCenterTtile.Left = displayCenterX - displayCenterTtile.ActualWidth / 2);
                Canvas.SetTop(displayCenterTtile, displayCenterTtile.Top = displayCenterY - displayCenterTtile.ActualHeight / 2);
                //displayCenterTtile.Opacity = 1;
                var opacityAnimationOfDCT = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                displayCenterTtile.BeginAnimation(MindMapTitle.OpacityProperty, opacityAnimationOfDCT);

                //    设置子标题的默认位置到中心。
                foreach (var child in childTitles)
                {
                    Canvas.SetLeft(child, displayCenterX - child.ActualWidth / 2);
                    Canvas.SetTop(child, displayCenterY - child.ActualHeight / 2);
                }

                if (parentTitle != null)
                {
                    Canvas.SetLeft(parentTitle, parentTitle.Left = (displayCenterTtile.TopCenter.X - parentTitle.ActualWidth / 2));
                    Canvas.SetTop(parentTitle, parentTitle.Top = spacing);

                    var parentTitleAnimation = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1))) { FillBehavior = FillBehavior.HoldEnd, };
                    parentTitle.BeginAnimation(MindMapTitle.OpacityProperty, parentTitleAnimation);
                }

                // 6. 根据当前中心节点的中心位置，计算出各子节点的实际左上角坐标

                for (int i = 0; i < middle; i++)
                {
                    // 右侧节点
                    var child = childTitles[i];
                    var left = displayCenterX + child.RelativePointX;
                    var top = displayCenterY + child.RelativePointY - child.ActualHeight / 2;
                    child.Left = left;
                    child.Top = top;
                    Canvas.SetLeft(child, left);
                    Canvas.SetTop(child, top);

                    var opacityAnimation = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    child.BeginAnimation(MindMapTitle.OpacityProperty, opacityAnimation);
                }

                for (int i = childTitles.Count - 1; i >= middle; i--)
                {
                    // 左侧节点
                    var child = childTitles[i];
                    var left = displayCenterX + child.RelativePointX - child.ActualWidth;
                    var top = displayCenterY + child.RelativePointY - child.ActualHeight / 2;
                    child.Left = left;
                    child.Top = top;
                    Canvas.SetLeft(child, left);
                    Canvas.SetTop(child, top);

                    var opacityAnimation = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    child.BeginAnimation(MindMapTitle.OpacityProperty, opacityAnimation);
                }

                // 7. 添加连接线
                //    向父节点的连接线
                if (parentTitle != null)
                {
                    displayCenterTtile.LinkPointStart = displayCenterTtile.TopCenter;
                    displayCenterTtile.LinkPoint3 = parentTitle.BottomCenter;
                    displayCenterTtile.LinkPoint1 = displayCenterTtile.LinkPoint2 =
                        new Point((displayCenterTtile.LinkPoint3.X - displayCenterTtile.LinkPointStart.X) / 2,
                                  (displayCenterTtile.LinkPoint3.Y - displayCenterTtile.LinkPointStart.Y) / 2);
                    displayCenterTtile.RefreshLine(null);

                    MainCanvas.Children.Add(displayCenterTtile.LinkToParentPath);

                    var opacityAnimationToParent = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    displayCenterTtile.LinkToParentPath.BeginAnimation(Path.OpacityProperty, opacityAnimationToParent);
                }

                //    右侧节点向中心显示节点的连接线
                for (int i = 0; i < middle; i++)
                {
                    // 右侧子节点
                    var child = childTitles[i];
                    var start = child.LinkPointStart = child.LeftCenter;
                    var end = child.LinkPoint3 = displayCenterTtile.RightCenter;
                    child.LinkPoint1 = new Point(end.X + (start.X - end.X) / 2, start.Y);
                    child.LinkPoint2 = new Point(end.X + (start.X - end.X) / 2, end.Y);

                    MainCanvas.Children.Add(child.LinkToParentPath);

                    child.LinkToParentPathFigure.StartPoint = child.LinkPointStart;
                    child.LinkToParentBezierSegment.Point1 = child.LinkPoint1;
                    child.LinkToParentBezierSegment.Point2 = child.LinkPoint2;
                    child.LinkToParentBezierSegment.Point3 = child.LinkPoint3;

                    child.RefreshLine(null);
                    var animationToParent = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    child.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animationToParent);
                }

                //    左侧节点向中心显示节点的连接线
                for (int i = childTitles.Count - 1; i >= middle; i--)
                {
                    // 左侧节点
                    var child = childTitles[i];
                    var start = child.LinkPointStart = child.RightCenter;
                    var end = child.LinkPoint3 = displayCenterTtile.LeftCenter;
                    child.LinkPoint1 = new Point(start.X + (end.X - start.X) / 2, start.Y);
                    child.LinkPoint2 = new Point(start.X + (end.X - start.X) / 2, end.Y);

                    MainCanvas.Children.Add(child.LinkToParentPath);

                    child.LinkToParentPathFigure.StartPoint = child.LinkPointStart;
                    child.LinkToParentBezierSegment.Point1 = child.LinkPoint1;
                    child.LinkToParentBezierSegment.Point2 = child.LinkPoint2;
                    child.LinkToParentBezierSegment.Point3 = child.LinkPoint3;

                    child.RefreshLine(null);
                    var animationToParent = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    child.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animationToParent);
                }

#if DEBUG
                LayoutEllipse.Height = LayoutEllipse.Width = layoutEllipseDiameter;
                Canvas.SetTop(LayoutEllipse, displayCenterY - layoutEllipseRadius);
                Canvas.SetLeft(LayoutEllipse, displayCenterX - layoutEllipseRadius);
                LayoutEllipse.Visibility = Visibility.Visible;
#endif

            }
            else
            {
                // 奇数个子级标题，其中索引值居中的那个标题是在底部的！
                middle = (int)middle;
                for (int i = 0; i < childTitles.Count; i++)
                {
                    if (i < middle)
                    {
                        rightSumHeight += spacing + childTitles[i].ActualHeight;
                    }
                    else if (i > middle)
                    {
                        leftSumHeight += spacing + childTitles[i].ActualHeight;
                    }
                    //else { ...} 这个底部居中
                }

                // 为实现左右两侧垂直居中，左右两侧子节点群需要考虑 offset
                var vOffset = Math.Abs(leftSumHeight - rightSumHeight) / 2;

                var maxHeight = Math.Max(leftSumHeight, rightSumHeight);
                var disSize = Math.Max(displayCenterTtile.ActualHeight, displayCenterTtile.ActualWidth);
                maxHeight = Math.Max(maxHeight, disSize);

                if (maxHeight > leftSumHeight && maxHeight > rightSumHeight)
                {
                    vOffset = (maxHeight - Math.Max(leftSumHeight, rightSumHeight)) / 2;
                }

                var midTitle = childTitles[(int)middle];
                var mainCanvasHeight = 0.0;
                if (childTitles.Count == 1)
                {
                    mainCanvasHeight = topSpacing + midTitle.ActualHeight + bottomSpacing; // 偶数时没有居中靠底部的子节点。
                    mainCanvasHeight += Math.Max(displayCenterTtile.ActualHeight, displayCenterTtile.ActualWidth);  // 这种情况下以此为直径
                }
                else
                {
                    mainCanvasHeight = topSpacing + maxHeight + midTitle.ActualHeight + bottomSpacing; // 偶数时没有居中靠底部的子节点。
                }

                if (parentTitle != null)
                {
                    mainCanvasHeight += parentTitle.ActualHeight;
                }
                MainCanvas.Height = mainCanvasHeight;

                // 3. 计算左右各个子节点的相对位置（相对于当前中心节点的中心）
                //    从 1 点钟方向→11 点钟方向
                double baseHeight = topSpacing + vOffset;
                double layoutEllipseDiameter = maxHeight;
                double layoutEllipseRadius = maxHeight / 2;  // 圆半径

                // 如果圆的直径小于 displayTitle.ActualWidth + spacing * 4（即中心标题过宽），提供 expandSpacing
                var expandSpacing = 0.0;
                if (layoutEllipseDiameter < displayCenterTtile.ActualWidth + spacing * 2)
                {
                    expandSpacing = displayCenterTtile.ActualWidth / 2 + spacing - layoutEllipseRadius;
                }

                if (childTitles.Count == 1)
                {
                    layoutEllipseDiameter = Math.Max(displayCenterTtile.ActualHeight, displayCenterTtile.ActualWidth);
                    layoutEllipseRadius = layoutEllipseDiameter / 2;
                }

                var centerY = topSpacing + layoutEllipseRadius;
                double maxRelativeRightWidth = 0;
                for (int i = 0; i < middle; i++)
                {
                    // 右侧子节点
                    double h = baseHeight + spacing + childTitles[i].ActualHeight / 2;
                    double sh = h - centerY;
                    if (Math.Abs(sh) < 10)
                    {
                        childTitles[i].RelativePointY = 0;
                        childTitles[i].RelativePointX = expandSpacing + layoutEllipseRadius + displayCenterTtile.ActualWidth / 2;  // 右侧节点相对中心的 X 差值总是正的
                    }
                    else
                    {
                        childTitles[i].RelativePointY = sh;          // 右侧节点相对于中心的 Y 差值有正有负
                        childTitles[i].RelativePointX = expandSpacing + Math.Sqrt(layoutEllipseRadius * layoutEllipseRadius - sh * sh) + displayCenterTtile.ActualWidth / 2;  // 右侧节点相对中心的 X 差值总是正的
                    }
                    maxRelativeRightWidth = Math.Max(childTitles[i].RelativePointX + childTitles[i].ActualWidth, maxRelativeRightWidth);
                    baseHeight += spacing + childTitles[i].ActualHeight;
                }

                // 索引居中的标题，其位置应该在底部居中
                midTitle.RelativePointY = layoutEllipseRadius;
                midTitle.RelativePointX = 0;

                // 因为是顺时针方向，左侧节点在逆序
                baseHeight = topSpacing + vOffset;
                double maxRelativeLeftWidth = 0;
                for (int i = childTitles.Count - 1; i > middle; i--)
                {
                    // 左侧节点
                    double h = baseHeight + spacing + childTitles[i].ActualHeight / 2;
                    double sh = h - centerY;
                    if (Math.Abs(sh) < 10)
                    {
                        childTitles[i].RelativePointY = 0;
                        childTitles[i].RelativePointX = -expandSpacing + -layoutEllipseRadius - displayCenterTtile.ActualWidth / 2;  // 右侧节点相对中心的 X 差值总是正的
                    }
                    else
                    {
                        childTitles[i].RelativePointY = sh;          // 右侧节点相对于中心的 Y 差值有正有负
                        childTitles[i].RelativePointX = -expandSpacing + -Math.Sqrt(layoutEllipseRadius * layoutEllipseRadius - sh * sh) - displayCenterTtile.ActualWidth / 2;  // 左侧节点相对中心的 X 差值总是负的
                    }
                    maxRelativeLeftWidth = Math.Max(-childTitles[i].RelativePointX + childTitles[i].ActualWidth, maxRelativeLeftWidth);
                    baseHeight += spacing + childTitles[i].ActualHeight;
                }

                // 4. 计算出 MainCanvas 需要的宽度

                if (childTitles.Count == 1)
                {
                    // 不这样处理，宽度会太小

                    MainCanvas.Width = spacing + Math.Max(displayCenterTtile.ActualWidth, childTitles[0].ActualWidth) + spacing;
                }
                else
                {
                    if (displayCenterTtile.ActualWidth > (maxHeight + spacing * 2))
                    {
                        // 如果中心标题很宽（超过了布局圆的直径）
                        // maxHeight 是布局圆的直径，因为已经加在 maxRelativeLeftWidth 和 maxRelativeRightWidth 中，不能重复加。
                        MainCanvas.Width = spacing + maxRelativeLeftWidth + spacing + (displayCenterTtile.ActualWidth - maxHeight) + spacing + maxRelativeRightWidth + spacing;    // 还要考虑中心标题的宽度
                    }
                    else
                    {
                        // maxHeight 是布局圆的直径，因为已经加在 maxRelativeLeftWidth 和 maxRelativeRightWidth 中，不能重复加。
                        MainCanvas.Width = spacing + maxRelativeLeftWidth + maxRelativeRightWidth + spacing;    // 还要考虑中心标题的宽度
                    }
                }

                var displayCenterX = spacing + maxRelativeLeftWidth;
                if (childTitles.Count == 1)
                {
                    displayCenterX = MainCanvas.Width / 2;
                }
                var displayCenterY = topSpacing + layoutEllipseRadius;
                if (parentTitle != null)
                {
                    displayCenterY += parentTitle.ActualHeight;
                }

                // 5. 根据 MainCanvas 的宽、高，计算出当前中心节点的中心位置和中心节点标题的左上角坐标

                Canvas.SetLeft(displayCenterTtile, displayCenterTtile.Left = displayCenterX - displayCenterTtile.ActualWidth / 2);
                Canvas.SetTop(displayCenterTtile, displayCenterTtile.Top = displayCenterY - displayCenterTtile.ActualHeight / 2);
                //displayCenterTtile.Opacity = 1;
                var opacityAnimationOfDCT = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                displayCenterTtile.BeginAnimation(MindMapTitle.OpacityProperty, opacityAnimationOfDCT);

                //    设置子标题的默认位置到中心。
                foreach (var child in childTitles)
                {
                    Canvas.SetLeft(child, displayCenterX - child.ActualWidth / 2);
                    Canvas.SetTop(child, displayCenterY - child.ActualHeight / 2);
                }

                if (parentTitle != null)
                {
                    Canvas.SetLeft(parentTitle, parentTitle.Left = displayCenterX - parentTitle.ActualWidth / 2);
                    Canvas.SetTop(parentTitle, parentTitle.Top = spacing);

                    var parentTitleAnimation = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1))) { FillBehavior = FillBehavior.HoldEnd, };
                    parentTitle.BeginAnimation(MindMapTitle.OpacityProperty, parentTitleAnimation);
                }

                // 6. 根据当前中心节点的中心位置，计算出各子节点的实际左上角坐标

                for (int i = 0; i < middle; i++)
                {
                    // 右侧子节点
                    var child = childTitles[i];
                    var left = displayCenterX + child.RelativePointX;
                    var top = displayCenterY + child.RelativePointY - child.ActualHeight / 2;
                    child.Left = left;
                    child.Top = top;
                    Canvas.SetLeft(child, left);
                    Canvas.SetTop(child, top);

                    var opacityAnimation = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    child.BeginAnimation(MindMapTitle.OpacityProperty, opacityAnimation);
                }

                // 索引居中的标题，其位置应该在底部居中
                //midTitle.RelativePointY = layoutEllipseRadius + midTitle.ActualHeight / 2;
                //midTitle.RelativePointX = 0;
                Canvas.SetLeft(midTitle, midTitle.Left = displayCenterX - midTitle.ActualWidth / 2);
                Canvas.SetTop(midTitle, midTitle.Top = displayCenterY + midTitle.RelativePointY);
                var opacityMidTitleAnimation = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                midTitle.BeginAnimation(MindMapTitle.OpacityProperty, opacityMidTitleAnimation);

                for (int i = childTitles.Count - 1; i > middle; i--)
                {
                    // 左侧节点
                    var child = childTitles[i];
                    var left = displayCenterX + child.RelativePointX - child.ActualWidth;
                    var top = displayCenterY + child.RelativePointY - child.ActualHeight / 2;
                    child.Left = left;
                    child.Top = top;
                    Canvas.SetLeft(child, left);
                    Canvas.SetTop(child, top);

                    var opacityAnimation = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    child.BeginAnimation(MindMapTitle.OpacityProperty, opacityAnimation);
                }

                // 7. 添加连接线
                //    向父节点的连接线
                if (parentTitle != null)
                {
                    displayCenterTtile.LinkPointStart = displayCenterTtile.TopCenter;
                    displayCenterTtile.LinkPoint3 = parentTitle.BottomCenter;
                    displayCenterTtile.LinkPoint1 = displayCenterTtile.LinkPoint2 =
                        new Point((displayCenterTtile.LinkPoint3.X - displayCenterTtile.LinkPointStart.X) / 2,
                                  (displayCenterTtile.LinkPoint3.Y - displayCenterTtile.LinkPointStart.Y) / 2);
                    displayCenterTtile.RefreshLine(null);

                    MainCanvas.Children.Add(displayCenterTtile.LinkToParentPath);

                    var opacityAnimationToParent = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    displayCenterTtile.LinkToParentPath.BeginAnimation(Path.OpacityProperty, opacityAnimationToParent);
                }

                //    右侧节点向中心显示节点的连接线
                for (int i = 0; i < middle; i++)
                {
                    // 右侧子节点
                    var child = childTitles[i];
                    var start = child.LinkPointStart = child.LeftCenter;
                    var end = child.LinkPoint3 = displayCenterTtile.RightCenter;
                    child.LinkPoint1 = new Point(end.X + (start.X - end.X) / 2, start.Y);
                    child.LinkPoint2 = new Point(end.X + (start.X - end.X) / 2, end.Y);

                    MainCanvas.Children.Add(child.LinkToParentPath);

                    child.LinkToParentPathFigure.StartPoint = child.LinkPointStart;
                    child.LinkToParentBezierSegment.Point1 = child.LinkPoint1;
                    child.LinkToParentBezierSegment.Point2 = child.LinkPoint2;
                    child.LinkToParentBezierSegment.Point3 = child.LinkPoint3;

                    child.RefreshLine(null);
                    var animationToParent = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    child.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animationToParent);
                }

                // 索引位置居中的标题的连接线
                midTitle.LinkPointStart = midTitle.TopCenter;
                midTitle.LinkPoint1 = midTitle.LinkPoint2 = new Point(midTitle.TopCenter.X, (midTitle.TopCenter.Y - displayCenterTtile.BottomCenter.Y) / 2 + displayCenterTtile.BottomCenter.Y);
                midTitle.LinkPoint3 = displayCenterTtile.BottomCenter;
                MainCanvas.Children.Add(midTitle.LinkToParentPath);

                midTitle.LinkToParentPathFigure.StartPoint = midTitle.LinkPointStart;
                midTitle.LinkToParentBezierSegment.Point1 = midTitle.LinkPoint1;
                midTitle.LinkToParentBezierSegment.Point2 = midTitle.LinkPoint2;
                midTitle.LinkToParentBezierSegment.Point3 = midTitle.LinkPoint3;

                midTitle.RefreshLine(null);

                var midAnimationToParent = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                midTitle.LinkToParentPath.BeginAnimation(Path.OpacityProperty, midAnimationToParent);

                //    左侧节点向中心显示节点的连接线
                for (int i = childTitles.Count - 1; i > middle; i--)
                {
                    // 左侧节点
                    var child = childTitles[i];
                    var start = child.LinkPointStart = child.RightCenter;
                    var end = child.LinkPoint3 = displayCenterTtile.LeftCenter;
                    child.LinkPoint1 = new Point(start.X + (end.X - start.X) / 2, start.Y);
                    child.LinkPoint2 = new Point(start.X + (end.X - start.X) / 2, end.Y);

                    MainCanvas.Children.Add(child.LinkToParentPath);

                    child.LinkToParentPathFigure.StartPoint = child.LinkPointStart;
                    child.LinkToParentBezierSegment.Point1 = child.LinkPoint1;
                    child.LinkToParentBezierSegment.Point2 = child.LinkPoint2;
                    child.LinkToParentBezierSegment.Point3 = child.LinkPoint3;

                    child.RefreshLine(null);
                    var animationToParent = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    child.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animationToParent);
                }
#if DEBUG
                LayoutEllipse.Height = LayoutEllipse.Width = layoutEllipseDiameter;
                Canvas.SetTop(LayoutEllipse, displayCenterY - layoutEllipseRadius);
                Canvas.SetLeft(LayoutEllipse, displayCenterX - layoutEllipseRadius);
                LayoutEllipse.Visibility = Visibility.Visible;
#endif

            }

            if (parentTitle != null)
            {
                Canvas.SetLeft(parentTitle, displayCenterTtile.TopCenter.X - parentTitle.ActualWidth / 2);
                Canvas.SetTop(parentTitle, spacing);

                displayCenterTtile.LinkPointStart = displayCenterTtile.TopCenter;
                displayCenterTtile.LinkPoint3 = parentTitle.BottomCenter;
                displayCenterTtile.LinkPoint2 = displayCenterTtile.LinkPoint1 = new Point(displayCenterTtile.TopCenter.X, (displayCenterTtile.TopCenter.Y - parentTitle.BottomCenter.Y) / 2 + parentTitle.BottomCenter.Y);
                displayCenterTtile.RefreshLine(null);

                if (MainCanvas.Children.Contains(displayCenterTtile.LinkToParentPath) == false)
                {
                    MainCanvas.Children.Add(displayCenterTtile.LinkToParentPath);
                }

                var parentTitleOpacityAnimationOfDCT = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                parentTitle.BeginAnimation(MindMapTitle.OpacityProperty, parentTitleOpacityAnimationOfDCT);

                var animationToDisplayParent = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                displayCenterTtile.LinkToParentPath.BeginAnimation(Path.OpacityProperty, animationToDisplayParent);
            }

            displayCenterTtile.Focus();
            displayCenterTtile.IsSelectedTitle = true;
            this.DisplayTitle = displayCenterTtile;
            this.SelectedTitle = displayCenterTtile;
        }

        /// <summary>
        /// 默认视图的加强版，显示当前节点、当前节点所有下级节点，不显示上级节点。
        /// </summary>
        /// <param name="element"></param>
        private void DisplayAllSubElementsWithDefaultView(TreeElement element)
        {
            if (element == null) return;
            if (element.Children.Count <= 0) return;

            if (element.ParentElement == null)
            {
                // 演示一级标题时，全部功能按钮均应可用
                btnFirst.IsEnabled = btnLast.IsEnabled = btnPreview.IsEnabled = btnNext.IsEnabled = true;
                pFirst.Fill = pLast.Fill = Brushes.LightCoral;
                pPreview.Fill = pNext.Fill = Brushes.DeepSkyBlue;

                btnParentElement.IsEnabled = false;
                pParent.Fill = Brushes.Gray;
            }
            else
            {
                // 截图按钮、保存图像到文件应保留，返回根标题按钮也应保留，其它应禁用
                btnFirst.IsEnabled = btnLast.IsEnabled = btnPreview.IsEnabled = btnNext.IsEnabled = false;
                pFirst.Fill = pLast.Fill = Brushes.Gray;
                pPreview.Fill = pNext.Fill = Brushes.Gray;

                btnParentElement.IsEnabled = true;
                pParent.Fill = Brushes.Firebrick;
            }

            HideAndRemoveTitles();

#if DEBUG
            LayoutEllipse.Visibility = Visibility.Hidden;
            MainCanvas.Children.Add(LayoutEllipse);
#endif

            // 默认视图的全功能版。显示当前节点、所有下级节点，不显示上级节点。
            // 上级节点居顶端、当前节点位于中间位置、直接子级分左右侧（但不按圆形排列），顺序从 1 点种附近到 11 点钟附近。
            // 过程：
            // 1. 先生成所有标题，放到容器（Canvas）中，这样就可得知所有标题所需要的空间尺寸

            // 2. 在递归的过程中确定各标题所需要的空间尺寸（包括自身和所有下级）
            var displayCenterTtile = BuildAndAddTitles(element, MindMapTitle.TitleRole.DisplayCenterTitle);

            var parent = element.ParentElement;
            var children = element.Children;

            displayCenterTtile.IsSelectedChanged += Title_IsSelectedChanged;

            var spacing = baseSpacingTimes * this.TitleDefaultFontSize;

            // 3. 根据所有直接子级需要的尺寸，判断应如何从 1 点钟方向顺时针排列。
            var mainCanvasHeight = 0.0;

            List<MindMapTitle> childTitles = displayCenterTtile.Children;
            if (childTitles.Count <= 0)
            {
                DisplayWithDefaultView(element);
                return;   // 没有子级没有显示的价值
            }

            List<MindMapTitle> leftChildren = new List<MindMapTitle>();
            List<MindMapTitle> rightChildren = new List<MindMapTitle>();

            var halfHeight = displayCenterTtile.DefaultNeedSize.Height / 2;
            var sumHeight = 0.0;
            var rightHeight = 0.0;
            var leftWidth = 0.0;
            var rightWidth = 0.0;
            for (var i = 0; i < childTitles.Count; i++)   // 这里的布局只需要考虑直接下级
            {
                var childTitle = childTitles[i];
                sumHeight += childTitle.DefaultNeedSize.Height;

                if (sumHeight > halfHeight && i > 0)
                {
                    childTitle.LocationDirection = MindMapTitle.Direction.Left;
                    leftChildren.Insert(0, childTitle);
                    leftWidth = Math.Max(leftWidth, childTitle.DefaultNeedSize.Width);
                }
                else
                {
                    childTitle.LocationDirection = MindMapTitle.Direction.Right;
                    rightChildren.Add(childTitle);
                    rightWidth = Math.Max(rightWidth, childTitle.DefaultNeedSize.Width);
                    rightHeight = sumHeight;
                }
            }

            var leftHeight = displayCenterTtile.DefaultNeedSize.Height - rightHeight;

            mainCanvasHeight = spacing + Math.Max(displayCenterTtile.ActualHeight, Math.Max(leftHeight, rightHeight)) + spacing;
            var leftBaseX = spacing + leftWidth;
            var rightBaseX = leftBaseX + spacing * 4 + displayCenterTtile.ActualWidth + spacing * 4;
            var mainCanvasWidth = rightBaseX + rightWidth + spacing;
            Point baseCenterPoint = new Point(spacing + leftWidth + spacing * 4 + displayCenterTtile.ActualWidth / 2, mainCanvasHeight / 2);
            var baseHeightOffset = Math.Abs(leftHeight - rightHeight) / 2;

            Canvas.SetLeft(displayCenterTtile, displayCenterTtile.Left = baseCenterPoint.X - displayCenterTtile.ActualWidth / 2);
            Canvas.SetTop(displayCenterTtile, displayCenterTtile.Top = baseCenterPoint.Y - displayCenterTtile.ActualHeight / 2);
            // displayCenterTtile.Opacity = 1;
            var opacityAnimation = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
            displayCenterTtile.BeginAnimation(Path.OpacityProperty, opacityAnimation);

            var ellipseSize = Math.Sqrt(displayCenterTtile.ActualHeight * displayCenterTtile.ActualHeight + displayCenterTtile.ActualWidth * displayCenterTtile.ActualWidth);
            var ellipse = new Ellipse()
            {
                Width = ellipseSize,
                Height = ellipseSize,
                Opacity = 0,
                Fill = null,
                Stroke = TitleDefaultBackground,
                StrokeThickness = 2,
            };
            MainCanvas.Children.Add(ellipse);
            Canvas.SetLeft(ellipse, baseCenterPoint.X - ellipseSize / 2);
            Canvas.SetTop(ellipse, baseCenterPoint.Y - ellipseSize / 2);
            // ellipse.Opacity = 1;
            var opacityAnimationOfEllipse = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
            ellipse.BeginAnimation(Path.OpacityProperty, opacityAnimationOfEllipse);

            Point baseRightPoint = new Point(rightBaseX, rightHeight < leftHeight ? spacing + baseHeightOffset : spacing);

            foreach (var title in rightChildren)
            {
                baseRightPoint = LayoutRightTitle(title, baseRightPoint, spacing);
                if (title.ParentTitle != null)
                {
                    title.LinkPointStart = title.LeftCenter;
                    title.LinkPoint3 = title.ParentTitle.RightCenter;
                    title.LinkPoint1 = new Point(title.ParentTitle.RightCenter.X + (title.LinkPointStart.X - title.LinkPoint3.X) / 2, title.LinkPointStart.Y);
                    title.LinkPoint2 = new Point(title.LinkPoint1.X, title.LinkPoint3.Y);
                    title.RefreshLine(null);
                    MainCanvas.Children.Add(title.LinkToParentPath);
                    // title.LinkToParentPath.Opacity = 1;
                    var opacityAnimationOfLink = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    title.LinkToParentPath.BeginAnimation(Path.OpacityProperty, opacityAnimationOfLink);
                }
            }

            Point baseLeftPoint = new Point(leftBaseX, leftHeight < rightHeight ? spacing + baseHeightOffset : spacing);
            foreach (var title in leftChildren)
            {
                baseLeftPoint = LayoutLeftTitle(title, baseLeftPoint, spacing);
                if (title.ParentTitle != null)
                {
                    title.LinkPointStart = title.RightCenter;
                    title.LinkPoint3 = title.ParentTitle.LeftCenter;
                    title.LinkPoint1 = new Point(title.RightCenter.X + (title.LinkPoint3.X - title.LinkPointStart.X) / 2, title.LinkPointStart.Y);
                    title.LinkPoint2 = new Point(title.LinkPoint1.X, title.LinkPoint3.Y);
                    title.RefreshLine(null);
                    MainCanvas.Children.Add(title.LinkToParentPath);
                    // title.LinkToParentPath.Opacity = 1;
                    var opacityAnimationOfLink = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                    title.LinkToParentPath.BeginAnimation(Path.OpacityProperty, opacityAnimationOfLink);
                }
            }

            MainCanvas.Width = mainCanvasWidth;
            MainCanvas.Height = mainCanvasHeight;

            displayCenterTtile.Focus();
            displayCenterTtile.IsSelectedTitle = true;
            this.DisplayTitle = displayCenterTtile;
            this.SelectedTitle = displayCenterTtile;

#if DEBUG
            MainCanvas.Background = Brushes.Red;
#endif
        }

        /// <summary>
        /// 返回所占据区域的左下角位置。
        /// </summary>
        private Point LayoutRightTitle(MindMapTitle title, Point basePoint, double spacing)
        {
            Canvas.SetLeft(title, title.Left = basePoint.X);
            Canvas.SetTop(title, title.Top = basePoint.Y + (title.DefaultNeedSize.Height - title.ActualHeight) / 2);
            // title.Opacity = 1;
            var opacityAnimation = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
            title.BeginAnimation(Path.OpacityProperty, opacityAnimation);

            var subBasePoint = new Point(basePoint.X + title.ActualWidth + spacing * 2, basePoint.Y);
            if (title.Children != null && title.Children.Count > 0)
            {
                foreach (var subChildTitle in title.Children)
                {
                    subBasePoint = LayoutRightTitle(subChildTitle, subBasePoint, spacing);
                    if (subChildTitle.ParentTitle != null)
                    {
                        subChildTitle.LinkPointStart = subChildTitle.LeftCenter;
                        subChildTitle.LinkPoint3 = subChildTitle.ParentTitle.RightCenter;
                        subChildTitle.LinkPoint1 = new Point(subChildTitle.ParentTitle.RightCenter.X + (subChildTitle.LinkPointStart.X - subChildTitle.LinkPoint3.X) / 2, subChildTitle.LinkPointStart.Y);
                        subChildTitle.LinkPoint2 = new Point(subChildTitle.LinkPoint1.X, subChildTitle.LinkPoint3.Y);
                        subChildTitle.RefreshLine(null);
                        MainCanvas.Children.Add(subChildTitle.LinkToParentPath);
                        // subChildTitle.LinkToParentPath.Opacity = 1;
                        var opacityAnimationOfLink = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                        subChildTitle.LinkToParentPath.BeginAnimation(Path.OpacityProperty, opacityAnimationOfLink);
                    }
                }
            }

            return new Point(basePoint.X, basePoint.Y + title.DefaultNeedSize.Height);
        }

        /// <summary>
        /// 返回所占据区域的右下角位置。
        /// </summary>
        private Point LayoutLeftTitle(MindMapTitle title, Point basePoint, double spacing)
        {
            Canvas.SetLeft(title, title.Left = basePoint.X - title.ActualWidth);
            Canvas.SetTop(title, title.Top = basePoint.Y + (title.DefaultNeedSize.Height - title.ActualHeight) / 2);
            // title.Opacity = 1;
            var opacityAnimation = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
            title.BeginAnimation(Path.OpacityProperty, opacityAnimation);

            var subBasePoint = new Point(basePoint.X - title.ActualWidth - spacing * 2, basePoint.Y);
            if (title.Children != null && title.Children.Count > 0)
            {
                foreach (var subChildTitle in title.Children)
                {
                    subBasePoint = LayoutLeftTitle(subChildTitle, subBasePoint, spacing);
                    if (subChildTitle.ParentTitle != null)
                    {
                        subChildTitle.LinkPointStart = subChildTitle.RightCenter;
                        subChildTitle.LinkPoint3 = subChildTitle.ParentTitle.LeftCenter;
                        subChildTitle.LinkPoint1 = new Point(subChildTitle.RightCenter.X + (subChildTitle.LinkPoint3.X - subChildTitle.LinkPointStart.X) / 2, subChildTitle.LinkPointStart.Y);
                        subChildTitle.LinkPoint2 = new Point(subChildTitle.LinkPoint1.X, subChildTitle.LinkPoint3.Y);
                        subChildTitle.RefreshLine(null);
                        MainCanvas.Children.Add(subChildTitle.LinkToParentPath);
                        // subChildTitle.LinkToParentPath.Opacity = 1;
                        var opacityAnimationOfLink = new DoubleAnimation(1, new Duration(TimeSpan.FromSeconds(1)));
                        subChildTitle.LinkToParentPath.BeginAnimation(Path.OpacityProperty, opacityAnimationOfLink);
                    }
                }
            }
            return new Point(basePoint.X, basePoint.Y + title.DefaultNeedSize.Height);
        }


        /// <summary>
        /// 递归，算出所有标题需要的尺寸（每个标题需要的尺寸是包括自己的所有下级的）。
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        private MindMapTitle BuildAndAddTitles(TreeElement element, MindMapTitle.TitleRole role)
        {
            if (element == null) return null;

            var title = new MindMapTitle(this, element, MindMapTitle.TitleRole.ChildTitle) { Opacity = 0, Role = role };
            MainCanvas.Children.Add(title);
            title.InvalidateMeasure();
            title.UpdateLayout();

            var spacing = this.TitleDefaultFontSize * BaseSpacingTimes;

            var sumHeight = 0.0;
            var maxWidthOfChildren = 0.0;
            if (element.Children != null && element.Children.Count > 0)
            {
                foreach (var child in element.Children)
                {
                    var childTitle = BuildAndAddTitles(child, MindMapTitle.TitleRole.ChildTitle);
                    if (title.Children == null)
                        title.Children = new List<MindMapTitle>();
                    title.Children.Add(childTitle);
                    childTitle.ParentTitle = title;

                    sumHeight += childTitle.DefaultNeedSize.Height;
                    maxWidthOfChildren = Math.Max(maxWidthOfChildren, childTitle.DefaultNeedSize.Width);
                }
            }

            var height = Math.Max(sumHeight, title.ActualHeight + spacing);
            var width = title.ActualWidth + spacing * 2 + maxWidthOfChildren;
            title.DefaultNeedSize = new Size(width, height);
            return title;
        }

        public enum ViewTheme
        {
            Light,
            Dark,
        }

        private ViewTheme theme = ViewTheme.Light;

        public ViewTheme Theme
        {
            get { return this.theme; }
            set
            {
                this.theme = value;
                RefreshTheme();
            }
        }

        public void RefreshTheme()
        {
            switch (this.theme)
            {
                case ViewTheme.Light:
                    {
                        this.Background =
                            MainCanvas.Background =
                            baseBoard.Background = Brushes.WhiteSmoke;

                        this.AncestorsTitleDefaultBackground = Brushes.CornflowerBlue;   // 正在演示的祖先级标题
                        this.TitleDefaultBackground = Brushes.Crimson;          // 正在演示的中心标题
                        this.ChildTitleDefaultBackground = Brushes.Black;       // 正在演示的子级标题

                        this.TitleDefaultForeground = Brushes.White;
                        this.TitleLinkStroke = Brushes.Black;

#if DEBUG
                        this.LayoutEllipse.Stroke =
#endif
                        this.TitleDefaultActiveBrush = Brushes.SandyBrown;
                        DisplayMindMap(curDisplayingElement);
                        break;
                    }
                case ViewTheme.Dark:
                    {
                        this.Background =
                            MainCanvas.Background =
                            baseBoard.Background = new SolidColorBrush(Color.FromRgb(64, 64, 64));

                        this.AncestorsTitleDefaultBackground = Brushes.BurlyWood;     // 正在演示的祖先级标题
                        this.TitleDefaultBackground = Brushes.LightBlue;              // 正在演示的中心标题
                        this.ChildTitleDefaultBackground = Brushes.White;             // 正在演示的子级标题

                        this.TitleDefaultForeground = Brushes.Black;
                        this.TitleLinkStroke = Brushes.White;

#if DEBUG
                        this.LayoutEllipse.Stroke =
#endif
                        this.TitleDefaultActiveBrush = Brushes.Crimson;
                        DisplayMindMap(curDisplayingElement);
                        break;
                    }
            }
        }

        private double baseSpacingTimes = 1.5;
        /// <summary>
        /// 标题之间的间距，由当前控件的默认字号的指定位数决定。
        /// </summary>
        public double BaseSpacingTimes
        {
            get
            {
                return baseSpacingTimes;
            }
            set { this.baseSpacingTimes = value; }
        }

        /// <summary>
        /// 开启非默认视图的办法是先选中某个标题（单击就可以），然后尝试按该视图呈献当前节点规定的内容。
        /// </summary>
        public enum ViewType
        {
            /// <summary>
            /// 默认（导图），中心放射状。显示当前标题（Element）自身、上级、子级。自身位居中心位置，上级在顶部，子级围绕自身呈圆形排列，自动分左右，从1点钟位置附近开始到11点附近位置结束。
            /// </summary>
            Default,

            /// <summary>
            /// 加强版的导图，显示当前节点及其下所有节点。
            /// </summary>
            MindMap,

            /// <summary>
            /// 上级列表，显示当前节点及其所有祖先级——直到当前根节点。由所需要的高度决定宽度，图片宽高比为 4:3。
            /// </summary>
            ListOfAncestors,

            /// <summary>
            /// 纵向列表，当前标题、父级标题在左上角，子级左对齐并垂直向右缩进一段距离的树型列表。
            /// </summary>
            List,

            /// <summary>
            /// 左开列表，向左开口的组织结构图
            /// </summary>
            OrgLeft,

            /// <summary>
            /// 右开列表，向右开口的树型结构图
            /// </summary>
            OrgRight,

            /// <summary>
            /// 上开列表，向上开口的树型结构图
            /// </summary>
            OrgUp,

            /// <summary>
            /// 下开列表，向下开口的树型结构图
            /// </summary>
            OrgDown,

            /// <summary>
            /// 左向钱骨图
            /// </summary>
            LeftFish,

            /// <summary>
            /// 右向鱼骨图
            /// </summary>
            RightFish,
        }

        private void btnFirst_Click(object sender, RoutedEventArgs e)
        {
            ShowFirstRootElement();
        }

        /// <summary>
        /// 显示第一个根节点。
        /// </summary>
        public void ShowFirstRootElement()
        {
            if (rootElements != null && rootElements.Count > 0)
            {
                DisplayMindMap(rootElements[0] as RootElement);
            }
            else
            {
                LMessageBox.ShowWarning("没有可生成思维导图的树型文字表内容！");
            }
        }

        /// <summary>
        /// 每个 RootElement 对应一张导图。这个用来记录当前是哪个 RootElement（或其下级节点）下正在演示。这样就可以根据这个值来前后切换了。
        /// </summary>
        private RootElement curDisplayingRootElement = null;
        /// <summary>
        /// 这两个值可能相同。
        /// </summary>
        private TreeElement curDisplayingElement = null;
        /// <summary>
        /// 当前正在演示的元素。
        /// </summary>
        public TreeElement CurDisplayingElement { get { return this.curDisplayingElement; } }

        /// <summary>
        /// 演示当前标题的图文资源子标题时需要用到。
        /// </summary>
        public Window MasterWindow { get; set; }

        private void btnPreview_Click(object sender, RoutedEventArgs e)
        {
            ShowPreviewRootElement();
        }

        /// <summary>
        /// 显示前一个根节点
        /// </summary>
        public void ShowPreviewRootElement()
        {
            if (curDisplayingRootElement == null) return;

            var index = RootElements.IndexOf(curDisplayingElement);

            if (index >= 1)
            {
                var preRootElement = RootElements[index - 1];
                DisplayMindMap(preRootElement);
            }
        }

        private void btnNext_Click(object sender, RoutedEventArgs e)
        {
            ShowNextRootElement();
        }

        /// <summary>
        /// 显示下一个根节点。
        /// </summary>
        public void ShowNextRootElement()
        {
            if (curDisplayingRootElement == null) return;

            var index = RootElements.IndexOf(curDisplayingElement);

            if (index >= 0 && index < RootElements.Count - 1)
            {
                var nextRootElement = RootElements[index + 1];
                DisplayMindMap(nextRootElement);
            }
        }

        private void btnLast_Click(object sender, RoutedEventArgs e)
        {
            ShowLastRootElement();
        }

        /// <summary>
        /// 显示最后一个根节点。
        /// </summary>
        public void ShowLastRootElement()
        {
            if (rootElements != null && rootElements.Count > 0)
            {
                DisplayMindMap(rootElements[rootElements.Count - 1] as RootElement);
            }
            else
            {
                LMessageBox.ShowWarning("没有可生成思维导图的树型文字表内容！");
            }
        }

        private void btnSnapIt_Click(object sender, RoutedEventArgs e)
        {
            SnapIt();
        }

        /// <summary>
        /// 截图到剪贴板。
        /// </summary>
        public void SnapIt()
        {
            if (rootElements == null || rootElements.Count <= 0 || CurDisplayingElement == null)
            {
                LMessageBox.ShowWarning("请先载入一个树型文字表！");
                return;
            }

            RenderTargetBitmap rtb;
            rtb = new RenderTargetBitmap((int)MainCanvas.ActualWidth, (int)MainCanvas.ActualHeight, 96, 96, PixelFormats.Pbgra32);
            //只在屏幕上显示的图片分辨率可以低些，96DPI就够了。

            rtb.Render(MainCanvas);

            try
            {
                Clipboard.SetImage(rtb);
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
            }
        }

        public enum ImageFormat { JPG, BMP, PNG, GIF, TIFF }
        /// <summary>
        /// 此方法来自：https://www.easck.com/cos/2022/0618/973727.shtml
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="format"></param>
        /// <param name="destStream"></param>
        /// <exception cref="InvalidOperationException"></exception>
        public static void GenerateImage(BitmapSource bitmap, ImageFormat format, System.IO.Stream destStream)
        {
            BitmapEncoder encoder = null;

            switch (format)
            {
                case ImageFormat.JPG:
                    encoder = new JpegBitmapEncoder();
                    break;
                case ImageFormat.PNG:
                    encoder = new PngBitmapEncoder();
                    break;
                case ImageFormat.BMP:
                    encoder = new BmpBitmapEncoder();
                    break;
                case ImageFormat.GIF:
                    encoder = new GifBitmapEncoder();
                    break;
                case ImageFormat.TIFF:
                    encoder = new TiffBitmapEncoder();
                    break;
                default:
                    throw new InvalidOperationException();
            }

            encoder.Frames.Add(BitmapFrame.Create(bitmap));
            encoder.Save(destStream);
        }

        private void btnSaveMindMapImage_Click(object sender, RoutedEventArgs e)
        {
            SaveMindMapImage();
        }

        /// <summary>
        /// 保存当前导图为文件。
        /// </summary>
        public void SaveMindMapImage()
        {
            RenderTargetBitmap rtb = new RenderTargetBitmap((int)MainCanvas.ActualWidth, (int)MainCanvas.ActualHeight, 96, 96, PixelFormats.Pbgra32);
            //只在屏幕上显示的图片分辨率可以低些，96DPI就够了。

            rtb.Render(MainCanvas);

            try
            {
                System.Windows.Forms.SaveFileDialog sfd = new System.Windows.Forms.SaveFileDialog() { Title = Globals.AppName, };
                sfd.Filter = "Jpg压缩图像格式(*.jpg)|*.jpg|PNG压缩图像格式(*.png)|*.png|BMP图像格式(*.bmp)|.bmp|GIF图像格式(*.gif)|*.gif|Tiff图像格式(*.tiff)|*.tiff";
                sfd.FilterIndex = 0;
                sfd.FileName = "LME导图-" + DateTime.Now.ToLongDateString().Replace(":", "-") + DateTime.Now.ToLongTimeString().Replace(":", "-");
                if (System.IO.Directory.Exists(Globals.PathOfWorkspaceAssetsFolder) == false)
                {
                    System.IO.Directory.CreateDirectory(Globals.PathOfWorkspaceAssetsFolder);
                }
                sfd.InitialDirectory = Globals.PathOfWorkspaceAssetsFolder;
                if (sfd.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;

                using (var fs = new System.IO.FileStream(sfd.FileName, System.IO.FileMode.OpenOrCreate))
                {
                    var format = ImageFormat.PNG; //默认PNG
                    var lastIndexOfDot = sfd.FileName.LastIndexOf('.');
                    if (lastIndexOfDot >= 0)
                    {
                        var subFix = sfd.FileName.Substring(lastIndexOfDot + 1).ToUpper();
                        if (Enum.TryParse<ImageFormat>(subFix, true, out ImageFormat result))
                        {
                            format = result;
                        }
                    }
                    GenerateImage(rtb, format, fs);
                }
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
            }
        }

        private void btnPreviewRootElement_Click(object sender, RoutedEventArgs e)
        {
            ShowCurrentRootElement();
        }

        /// <summary>
        /// 显示当前节点所在的根节点。
        /// </summary>
        public void ShowCurrentRootElement()
        {
            if (curDisplayingRootElement != null)
            {
                DisplayMindMap(curDisplayingRootElement);
            }
        }

        public event EventHandler<RoutedEventArgs> LoadTreeButtonClicked;

        protected void OnLoadTreeButtonClicked(object sender, RoutedEventArgs e)
        {
            if (LoadTreeButtonClicked != null)
            {
                LoadTreeButtonClicked(sender, e);
            }
        }

        private void btnLoadTree_Click(object sender, RoutedEventArgs e)
        {
            OnLoadTreeButtonClicked(sender, e);
        }

        private void btnParentElement_Click(object sender, RoutedEventArgs e)
        {
            ShowParentElement();
        }

        /// <summary>
        /// 显示当前节点的上级节点。
        /// </summary>
        public void ShowParentElement()
        {
            if (curDisplayingElement != null && curDisplayingElement.ParentElement != null)
            {
                DisplayMindMap(curDisplayingElement.ParentElement);
            }
        }

        private void btnPreviewAsDefaultMindMapView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            L.GetKeyboardState(out bool isAlt, out bool isShift, out bool isCtrl);
            if (isCtrl && !isShift && !isAlt)
            {
                PreviewAsFullMindMapView((sender as Button).Tag.ToString(), true);
            }
            else
            {
                PreviewAsDefaultMindMapView((sender as Button).Tag.ToString(), true);
            }
        }

        /// <summary>
        /// 按默认导图视图显示。
        /// </summary>
        public void PreviewAsDefaultMindMapView(string viewTypeText, bool keepCurDisplayingTitle)
        {
            if (MainCanvas == null || MainCanvas.Children == null || MainCanvas.Children.Count <= 0) return;

            MindMapTitle selectedTitle = null;
            if (keepCurDisplayingTitle)
            {
                this.SelectedTitle = this.DisplayTitle;
                this.DisplayTitle.IsSelectedTitle = true;
                selectedTitle = this.DisplayTitle;
            }
            else
            {
                foreach (var ue in MainCanvas.Children)
                {
                    var title = ue as MindMapTitle;
                    if (title == null) continue;

                    if (title.IsSelectedTitle)
                    {
                        selectedTitle = title;
                        break;
                    }
                }
            }

            if (selectedTitle == null)
            {
                LMessageBox.ShowWarning("请先用鼠标左键点击选中一个节点！如果尚未载入树型文字表，请先载入！");
                return;
            }

            if (Enum.TryParse<ViewType>(viewTypeText, true, out ViewType viewType))
            {
                this.DisplayView = viewType;
            }

            L.GetKeyboardState(out bool isAlt, out bool isShift, out bool isCtrl);

            this.DisplayView = ViewType.Default;

            // 单击，只显示上级（如果有）、当前级、子级。
            DisplayMindMap(selectedTitle.Element);
            spViews.Visibility = Visibility.Collapsed;

            OnPreviewed(this, new PreviewedArgs() { ViewType = this.displayView, NewElement = selectedTitle.Element });
        }

        /// <summary>
        /// 按完全导图视图显示。
        /// </summary>
        public void PreviewAsFullMindMapView(string viewTypeText, bool keepCurDisplayingTitle)
        {
            if (MainCanvas == null || MainCanvas.Children == null || MainCanvas.Children.Count <= 0) return;

            MindMapTitle selectedTitle = null;
            if (keepCurDisplayingTitle)
            {
                this.SelectedTitle = this.DisplayTitle;
                this.DisplayTitle.IsSelectedTitle = true;
                selectedTitle = this.DisplayTitle;
            }
            else
            {
                foreach (var ue in MainCanvas.Children)
                {
                    var title = ue as MindMapTitle;
                    if (title == null) continue;

                    if (title.IsSelectedTitle)
                    {
                        selectedTitle = title;
                        break;
                    }
                }
            }

            if (selectedTitle == null)
            {
                LMessageBox.ShowWarning("请先用鼠标左键点击选中一个节点！如果尚未载入树型文字表，请先载入！");
                return;
            }

            if (Enum.TryParse<ViewType>(viewTypeText, true, out ViewType viewType))
            {
                this.DisplayView = viewType;
            }

            L.GetKeyboardState(out bool isAlt, out bool isShift, out bool isCtrl);

            this.DisplayView = ViewType.MindMap;

            // 双击，不显示上级、当前级、当级级的所有下级（含子级）。
            DisplayMindMap(selectedTitle.Element, true);
            spViews.Visibility = Visibility.Collapsed;

            OnPreviewed(this, new PreviewedArgs() { ViewType = this.displayView, NewElement = selectedTitle.Element });
        }

        private void btnSnapItAndInsertToDocument_Click(object sender, RoutedEventArgs e)
        {
            if (rootElements == null || rootElements.Count <= 0 || CurDisplayingElement == null)
            {
                LMessageBox.ShowWarning("请先载入一个树型文字表！");
                return;
            }

            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null)
            {
                LMessageBox.ShowWarning("请选打开一个文档！");
                return;
            }

            RenderTargetBitmap rtb;
            rtb = new RenderTargetBitmap((int)MainCanvas.ActualWidth, (int)MainCanvas.ActualHeight, 96, 96, PixelFormats.Pbgra32);
            //只在屏幕上显示的图片分辨率可以低些，96DPI就够了。

            rtb.Render(MainCanvas);

            try
            {
                Clipboard.SetImage(rtb);
                if (Theme == ViewTheme.Light)
                {
                    ae.TryToPasteResourceFileLink();
                }
                else
                {
                    ae.TryToPasteResourceFileLink();
                }
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
            }
        }

        private void btnShowTools_Click(object sender, RoutedEventArgs e)
        {
            if (spViews.Visibility == Visibility.Visible)
            {
                //spDisplayTools.Visibility = Visibility.Collapsed;
                spViews.Visibility = Visibility.Collapsed;
            }
            else
            {
                //spDisplayTools.Visibility = Visibility.Visible;
                spViews.Visibility = Visibility.Visible;
            }
        }

        private void btnPreviewAsOrgView_Click(object sender, RoutedEventArgs e)
        {
            var viewTypeName = (sender as Button).Tag.ToString();
            PreviewAsOrgView(viewTypeName, true);
        }

        /// <summary>
        /// 按组织结构图视图显示（四向）。
        /// </summary>
        public void PreviewAsOrgView(string viewTypeText, bool keepCurDisplayingTitle)
        {
            if (MainCanvas == null || MainCanvas.Children == null || MainCanvas.Children.Count <= 0) return;

            MindMapTitle selectedTitle = null;
            if (keepCurDisplayingTitle)
            {
                this.SelectedTitle = this.DisplayTitle;
                this.DisplayTitle.IsSelectedTitle = true;
                selectedTitle = this.DisplayTitle;
            }
            else
            {
                foreach (var ue in MainCanvas.Children)
                {
                    var title = ue as MindMapTitle;
                    if (title == null) continue;

                    if (title.IsSelectedTitle)
                    {
                        selectedTitle = title;
                        break;
                    }
                }
            }

            if (selectedTitle == null)
            {
                LMessageBox.ShowWarning("请先用鼠标左键点击选中一个节点！如果尚未载入树型文字表，请先载入！");
                return;
            }

            if (Enum.TryParse<ViewType>(viewTypeText, true, out ViewType viewType))
            {
                this.DisplayView = viewType;
            }

            DisplayWithOrgView(selectedTitle.Element, this.DisplayView);
            spViews.Visibility = Visibility.Collapsed;

            OnPreviewed(this, new PreviewedArgs() { ViewType = this.displayView, NewElement = selectedTitle.Element });
        }

        private void btnPreviewAsFishView_Click(object sender, RoutedEventArgs e)
        {
            PreviewAsFishBone((sender as Button).Tag.ToString(), true);
        }

        /// <summary>
        /// 按鱼骨图视图显示（左右向）。
        /// </summary>
        public void PreviewAsFishBone(string viewTypeText, bool keepCurDisplayingTitle)
        {
            if (MainCanvas == null || MainCanvas.Children == null || MainCanvas.Children.Count <= 0) return;

            MindMapTitle selectedTitle = null;
            if (keepCurDisplayingTitle)
            {
                this.SelectedTitle = this.DisplayTitle;
                this.DisplayTitle.IsSelectedTitle = true;
                selectedTitle = this.DisplayTitle;
            }
            else
            {
                foreach (var ue in MainCanvas.Children)
                {
                    var title = ue as MindMapTitle;
                    if (title == null) continue;

                    if (title.IsSelectedTitle)
                    {
                        selectedTitle = title;
                        break;
                    }
                }
            }

            if (selectedTitle == null)
            {
                LMessageBox.ShowWarning("请先用鼠标左键点击选中一个节点！如果尚未载入树型文字表，请先载入！");
                return;
            }

            if (Enum.TryParse<ViewType>(viewTypeText, true, out ViewType viewType))
            {
                this.DisplayView = viewType;
            }

            DisplayWithFishView(selectedTitle.Element, this.DisplayView);
            spViews.Visibility = Visibility.Collapsed;

            OnPreviewed(this, new PreviewedArgs() { ViewType = this.displayView, NewElement = selectedTitle.Element });
        }

        internal void Refresh()
        {
            if (curDisplayingElement != null)
            {
                DisplayMindMap(curDisplayingElement);
            }
        }

        private void btnPreviewAsListView_Click(object sender, RoutedEventArgs e)
        {
            PreviewAsListView((sender as Button).Tag.ToString(), true);
        }

        /// <summary>
        /// 按纵向列表视图显示。
        /// </summary>
        public void PreviewAsListView(string viewTypeText, bool keepCurDisplayingTitle)
        {
            if (MainCanvas == null || MainCanvas.Children == null || MainCanvas.Children.Count <= 0) return;

            MindMapTitle selectedTitle = null;
            if (keepCurDisplayingTitle)
            {
                this.SelectedTitle = this.DisplayTitle;
                this.DisplayTitle.IsSelectedTitle = true;
                selectedTitle = this.DisplayTitle;
            }
            else
            {
                foreach (var ue in MainCanvas.Children)
                {
                    var title = ue as MindMapTitle;
                    if (title == null) continue;

                    if (title.IsSelectedTitle)
                    {
                        selectedTitle = title;
                        break;
                    }
                }
            }

            if (selectedTitle == null)
            {
                LMessageBox.ShowWarning("请先用鼠标左键点击选中一个节点！如果尚未载入树型文字表，请先载入！");
                return;
            }

            if (Enum.TryParse<ViewType>(viewTypeText, true, out ViewType viewType))
            {
                this.DisplayView = viewType;
            }

            DisplayWithListView(selectedTitle.Element);
            spViews.Visibility = Visibility.Collapsed;

            OnPreviewed(this, new PreviewedArgs() { ViewType = this.displayView, NewElement = selectedTitle.Element });
        }

        private void btnPreviewAsAncestorView_Click(object sender, RoutedEventArgs e)
        {
            PreviewAsAncestorView((sender as Button).Tag.ToString(), true);
        }

        /// <summary>
        /// 显示所有上级节点。
        /// </summary>
        public void PreviewAsAncestorView(string viewTypeText, bool keepCurDisplayingTitle)
        {
            if (MainCanvas == null || MainCanvas.Children == null || MainCanvas.Children.Count <= 0) return;

            MindMapTitle selectedTitle = null;
            if (keepCurDisplayingTitle)
            {
                this.SelectedTitle = this.DisplayTitle;
                this.DisplayTitle.IsSelectedTitle = true;
                selectedTitle = this.DisplayTitle;
            }
            else
            {
                foreach (var ue in MainCanvas.Children)
                {
                    var title = ue as MindMapTitle;
                    if (title == null) continue;

                    if (title.IsSelectedTitle)
                    {
                        selectedTitle = title;
                        break;
                    }
                }
            }

            if (selectedTitle == null)
            {
                LMessageBox.ShowWarning("请先用鼠标左键点击选中一个节点！如果尚未载入树型文字表，请先载入！");
                return;
            }

            if (Enum.TryParse<ViewType>(viewTypeText, true, out ViewType viewType))
            {
                this.DisplayView = viewType;
            }

            DisplayWithAncestorsList(selectedTitle.Element);
            spViews.Visibility = Visibility.Collapsed;

            OnPreviewed(this, new PreviewedArgs() { ViewType = this.displayView, NewElement = selectedTitle.Element });
        }

        /// <summary>
        /// 演示当前选中的节点所有直接下级的图文资源节点。
        /// </summary>
        public void DemonstratResources()
        {
            var st = this.SelectedTitle;
            if (st == null || st.Element == null)
            {
                LMessageBox.ShowWarning("请选中某个节点！");
                return;
            }

            if (st.Element.Children == null || st.Element.Children.Count <= 0)
            {
                LMessageBox.ShowWarning("当前选定节点没有子节点，无法演示图文节点！");
                return;
            }

            var groups = new List<GraphicDemonstrator.DemoResourceGroup>();
            var group = new GraphicDemonstrator.DemoResourceGroup();
            foreach (var child in st.Element.Children)
            {
                switch (child.Type)
                {
                    case TreeElementType.ImageLink:
                        {
                            group.Add(new GraphicDemonstrator.DemoResourceInfo()
                            {
                                RelativePath = child.RelativePath,
                                // 这种情况必须手工计算出当前图像（或文档）链接的真实路径。
                                AbsolutePath = child.LinkedImageRealPath,
                                LinkType = "Image",
                                LinkTitle = child.Content,
                            });
                            break;
                        }
                    case TreeElementType.FileLink:
                        {
                            group.Add(new GraphicDemonstrator.DemoResourceInfo()
                            {
                                RelativePath = child.RelativePath,
                                // 这种情况必须手工计算出当前图像（或文档）链接的真实路径。
                                AbsolutePath = child.LinkdeFileRealPath,
                                LinkType = "Html",
                                LinkTitle = child.Content,
                            });
                            break;
                        }
                }
            }

            if (group.Count > 0)
            {
                groups.Add(group);
            }

            if (groups.Count > 0)
            {
                var win = new GraphicDemonstrator.GraphicDemonstrator(Globals.MainWindow.FormatDemoResourceGroups(groups))
                {
                    Owner = this.MasterWindow,
                    ThemeText = Globals.MainWindow.ThemeText,
                };
                win.ShowDialog();
            }
            else
            {
                LMessageBox.ShowWarning("当前节点下没有图文资源节点！");
                return;
            }
        }

        private void btnDemonstratResources_Click(object sender, RoutedEventArgs e)
        {
            DemonstratResources();
        }
    }

    public class ViewtypeChangedArgs : EventArgs
    {
        public MindMapView.ViewType OldViewType { get; set; }
        public MindMapView.ViewType NewViewType { get; set; }
    }

    public class DisplayElementChangedArgs : EventArgs
    {
        public TreeElement OldElement { get; set; } = null;
        public TreeElement NewElement { get; set; } = null;
    }

    public class PreviewedArgs : EventArgs
    {
        public MindMapView.ViewType ViewType { get; set; }
        public TreeElement NewElement { get; set; } = null;
    }
}
